g++ 4.9.2 regression on pass reference to 'this'

247 Views Asked by At

This is a minimized part of Pointer to implementation code:

template<typename T>
class PImpl {
private:
  T* m;
public:

  template<typename A1> 
  PImpl(A1& a1) : m(new T(a1)) {
  }
};

struct A{
    struct AImpl;
    PImpl<AImpl> me;
    A();
};

struct A::AImpl{
    const A* ppub;
    AImpl(const A* ppub)
    :ppub(ppub){}
};
A::A():me(this){}

A a;
int main (int, char**){
    return 0;
}

It compilable on G++4.8 and prior and works as well. But G++4.9.2 compiler raise following errors:

prog.cpp: In constructor 'A::A()':
prog.cpp:24:15: error: no matching function for call to 'PImpl<A::AImpl>::PImpl(A*)'
 A::A():me(this){}
               ^
prog.cpp:24:15: note: candidates are:
prog.cpp:9:5: note: PImpl<T>::PImpl(A1&) [with A1 = A*; T = A::AImpl]
   > PImpl(A1& a1) : m(new T(a1)) {
     ^
prog.cpp:9:5: note:   no known conversion for argument 1 from 'A*' to 'A*&'
prog.cpp:2:7: note: PImpl<A::AImpl>::PImpl(const PImpl<A::AImpl>&)
 class PImpl {
       ^
prog.cpp:2:7: note:   no known conversion for argument 1 from 'A*' to 'const PImpl<A::AImpl>&'

But it can be fixed by small hack. If i pass '&*this' instead of 'this' then it bring to compilable state.

Is it G++ regression or new C++ standards feature which eliminate backward compatibility?

2

There are 2 best solutions below

0
On

This didn't compile for me with gcc-4.6, so it seems that gcc-4.8 is where the regression occurred. It seems what you want is to take A1 by universal reference, ie: PImpl(A1 && a1). This compiles for me with both gcc-4.6, gcc-4.8 and gcc-4.9.

3
On

We can make a simpler example that compiles on neither g++ 4.9 nor clang:

template <typename T>
void call(T& ) {  }

struct A { 
    void foo() { call(this); }
};

int main()
{
    A().foo();
}

That is because this is, from the standard, [class.this] (§9.3.2):

In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value is the address of the object for which the function is called.

You cannot take an lvalue reference to a prvalue, hence the error - which gcc explains better than clang in this case:

error: invalid initialization of non-const reference of type A*& from an rvalue of type A*

If we rewrite call to either take a const T& or a T&&, both compilers accept the code.