Look at this code:
template <typename T, void (T::*pfn)()> struct Testee {};
class Tester
{
private:
void foo() {}
public:
using type_t = Testee<Tester, &Tester::foo>;
};
It successfully compiles with g++ -std=c++14 -Wall -Wextra
.
However, when I change the order of foo
and type_t
, an error occurs:
$ cat test.cpp
template <typename T, void (T::*pfn)()> struct Testee {};
class Tester
{
public:
using type_t = Testee<Tester, &Tester::foo>;
private:
void foo() {}
};
int main()
{
}
$ g++ -std=c++14 -Wall -Wextra -pedantic test.cpp
test.cpp:6:36: error: incomplete type ‘Tester’ used in nested name specifier
using type_t = Testee<Tester, &Tester::foo>;
^
test.cpp:6:47: error: template argument 2 is invalid
using type_t = Testee<Tester, &Tester::foo>;
^
Usually, the order of declarations in class definitions has no effect on name resolving. For example:
struct A // OK
{
void foo(int a = val) { }
static constexpr const int val = 42;
};
struct B // OK
{
static constexpr const int val = 42;
void foo(int a = val) { }
};
However, it has an effect in this case. Why?
This is not really related to templates. You get a similar error on:
class Tester
{
public:
using type_t = decltype(&Tester::foo);
private:
void foo() {}
};
It's true that a class is (Standard 9.2/2):
regarded as complete within function bodies, default arguments, using-declarations introducing inheriting constructors (12.9), exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes).
However, the definition of a member type is not in that list, so it can only use names declared before that point.