C++ Logo


Advanced search

Type requirements for va_start

From: Lukas Barth <cpp_at_[hidden]>
Date: Tue, 25 Apr 2023 20:53:46 +0200

The rules for `va_start` in `[cstdarg.syn]/1.2` [1] (referring to C++20
here) state:

> If the parameter parmN is of a reference type, or of a type that is
> not compatible with the type that results when passing an argument
> for which there is no parameter, the behavior is undefined.

I have trouble understanding this rule. I think I have two problems:

1.) What does "compatible" mean in this context?

Does "type a is compatible with type b" mean "layout compatible"? Does
it mean "an object of type a may be accessed through a pointer to type
b, and vice versa" (this would essentially mean that they may be
aliased, right?)

The C standard actually has (rather complicated) rules for what
"compatible types" are in section "6.2.7 Compatible type and composite
type". However, the C++ standard explicitly says in `[diff.basic]/5`

> C allows “compatible types” in several places, C++ does not.

Could it be that this use of "compatible" in `[stdarg.syn]/1.2` was
grandfathered in from C, but is not defined anymore?

2.) What is "the type that results when passing an argument for which
there is no parameter"? Type of what? `paramN`? Say I have

void foo(int i, ...) {
   std::va_list args;
   va_start(args, i);

Now the "when passing an argument for which there is no parameter" part
means that I should consider a situation like this, correct?

SomeType t;
int x = 42;
foo(x, t);

In other words: the variadic argument list should *not* be empty. In
this situation, I must figure out "the type that results", and that
must be "compatible" with `int`. I see two possible cases: (a) "the
type that results" refers to the type of `i` in `foo`. (b) "the type
that results" refers to the type of the first argument in the variadic
argument list, after promotions etc.

(a) does make little sense, since the type of `i` is always `int`, and
that is of course compatible with itself.

(b) also does not make any sense, because that would mean that the type
of the first variadic argument must always be "compatible with" the
last non-variadic parameter. That would e.g. mean that the second
argument to `printf` must *always* be "compatible with" `const char *`.
Either I've seen *a lot* of ill-formed usages of `printf`, or my
interpretation (b) is nonsense.

At this point I'm at my wit's end. How is that rule to be interpreted?

Thanks for any hints,

P.S.: I asked more or less the same question a while ago on
StackOverflow - if you collect internet points, you can of course also
answer over there:

[1] https://www.eel.is/c++draft/cstdarg.syn#1.2
[2] https://www.eel.is/c++draft/diff.basic#5

Received on 2023-04-25 18:54:31