ikh
ikh

Reputation: 10417

Why is declaration order important for passing a member function pointer as a template argument?

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?

Upvotes: 23

Views: 1216

Answers (2)

Arne Vogel
Arne Vogel

Reputation: 6676

Usually, the order of declaration in class definition have no effects.

That's quite an overstatement. A few uses of declarations that appear later in the class definition are allowed, to the best of my knowledge:

  • default arguments (as you mentioned; but not default template arguments)
  • uses within a function body, function-try-block or member initializer
  • in-class initializers (C++11 or later)

Also, as has been mentioned, the order of data members affects construction and destruction order. Also, reordering stuff between translation units may surprisingly cause an ODR violation.

Upvotes: 0

aschepler
aschepler

Reputation: 72271

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.

Upvotes: 33

Related Questions