Code below compiles and works both in Visual Studio and g++:
class A;
A* getRef(void);
char (&func(...))[2];
int main() {
bool isDefault = sizeof(func(getRef()))==2;
std::cout << isDefault << std::endl; //prints 1
return 0;
}
Next code still compiles (and works) in Studio, but g++ states this is invalid use of incomplete type 'class A'
:
class A;
A& getRef(void); //the only change
char (&func(...))[2];
int main() {
bool isDefault = sizeof(func(getRef()))==2; //g++ error here
std::cout << isDefault << std::endl;
return 0;
}
Is this a fundamentally wrong code which should be rejected by a compiler (and if so, why VS compiler doesn't produce any warnings)? Or is there actually some difference between pointers and references in this context that I'm not aware of?
The crucial aspect of the modified code is that a reference to incomplete type is passed as actual argument to an ellipsis formal argument.
C++11 §5.2.2/7, about arguments to ellipsis:” The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. An argument that has (possibly cv-qualified) type
std::nullptr_t
is converted to typevoid*
(4.10). After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed.
Then for the lvalue-to-rvalue conversion, we find
C++11 §4.1/1:” A glvalue (3.10) of a non-function, non-array type
T
can be converted to a prvalue. IfT
is an incomplete type, a program that necessitates this conversion is ill-formed.
As I read it, but this is just my gut-feeling interpretation, a reference to T
is a glvalue of type T
, which here is incomplete, yielding ill-formed code.
No matter what the arguments to func
are sizeof(func(getRef()))
is going to be equal to 2
. The arguments to func
need not be evaluated to come up with that answer.
From the C++11 Standard:
5.3.3 Sizeof
1 The
sizeof
operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is an unevaluated operand (Clause 5), or a parenthesized type-id.
In your case, the operand is an expression, which is an unevaluated operand. The compiler need not evaluate func(getRef())
to come up with the value of sizeof(func(getRef()))
.
Hence, I conclude that g++
is overreaching in what it needs.
It appears that g++'s handling of variable argument functions is the culprit. It works fine when func
is modified to be
char (&func(A&))[2];
See it working at http://ideone.com/YQD9v0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With