Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invalid use of incomplete type, reference vs pointer

Tags:

c++

g++

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?

like image 786
Abstraction Avatar asked Aug 29 '16 17:08

Abstraction


2 Answers

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 type void* (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. If T 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.

like image 89
Cheers and hth. - Alf Avatar answered Oct 15 '22 08:10

Cheers and hth. - Alf


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.

like image 31
R Sahu Avatar answered Oct 15 '22 07:10

R Sahu