I got the errors compiling this code with g++ 4.6 and 4.8.
g++ 4.2 and 4.4 is OK. Is it a bug or some new language feature?
template <typename T>
struct A { typedef typename T::value_type type; };
template <typename U>
struct B
{
void bar () { }
void foo ()
{
// OK
this->bar ();
// OK
(*this).bar ();
// Error in g++ 4.6-4.8
// leads to full instantiating of template arg "U"
(&*this)->bar ();
}
};
int main ()
{
B< A<void> > b;
b.foo ();
return 0;
}
g++ inst.cc
inst.cc: In instantiation of ‘struct A<void>’:
inst.cc:20:5: required from ‘void B<U>::foo() [with U = A<void>]’
inst.cc:27:10: required from here
inst.cc:3:34: error: ‘void’ is not a class, struct, or union type
typedef typename T::value_type type;
^
Update 1: A cannot be instantiated, I know.
The question is: why the compiler tries to instantiate it at “(&*this)->bar ()” line, but not at “this->bar ()” or “(*this).bar ()” lines?
Update 2:
The suggested workaround with addressof (object) is not working for me, because actually I got the error when I tried to use std::bind (&B::bar, this). The real code is much more complex of course and the bind was not used standalone, but the problem was traced to the simple std::bind expression.
I did not want to rewrite or reinvent std::bind, so I had to use CRTP to make it work:
#include <tr1/functional>
template <typename T>
struct A { typedef typename T::value_type type; };
template <typename Derived, typename U>
struct B
{
Derived* derived (void) { return static_cast<Derived*>(this); }
void bar () { }
void foo ()
{
// error with recent compiler.
// std::tr1::bind (&B::bar, this) ();
// now ok
std::tr1::bind (&Derived::bar, derived ()) ();
}
};
struct C: B<C, A<void> >
{
};
int main ()
{
C c;
c.foo ();
return 0;
}
I find such errors and workarounds to be completely illogical though.
Analysis/explanation:
What you are seeing is shallow instantiation, not full (see below for proof).
ADL is the culprit here.
The standard backs me up here: §3.4.2 (p46 in n3337):
The bolded phrase includes
class A<void>as a lookup namespace for ADL.Workaround:
In your situation
std::addressof(b)can be used instead of&band it will work.Demonstration:
See http://liveworkspace.org/code/4f85a06598eebe1d8060112be36f4a29
Note: the
(unqualified-id)trick is defined in §3.4.2 of the standard)Prints
Also, you can verify that the template argument (
A<void>) gets shallow instantiated only. Moving the ill-formed typedef into a member function removes the problem:Outputs (http://liveworkspace.org/code/a15c933293281d0926e8b1ff39180079)
History:
operator&was the problem, butstd::addressof()was ok!This lead me to my ‘Hypothesis II’ (see above)