23

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?

Boann
  • 48,794
  • 16
  • 117
  • 146
ikh
  • 10,119
  • 1
  • 31
  • 70
  • Have you tried a different compiler? – max Jun 09 '17 at 11:11
  • 5
    "the order of declaration in class definition have no effects" afaik this isnt quite true in general, eg. members are constructed in the order they are declared – 463035818_is_not_an_ai Jun 09 '17 at 11:13
  • @tobi303 in this scenario there is no construction (i.e. no ctor called at all) – Luca Cappa Jun 09 '17 at 11:15
  • @LucaCappa I know, I just said that in general the statement isnt true – 463035818_is_not_an_ai Jun 09 '17 at 11:15
  • @max Well, I tried clang++ in [coliru](http://coliru.stacked-crooked.com), and it works the same. – ikh Jun 09 '17 at 11:17
  • ctor invokation order matters, in this case it shouldnt. But I guess that accessing the pointer of a function not yet declared (i.e. &foo() ) is the issue here. – Luca Cappa Jun 09 '17 at 11:17
  • Your example with the default parameter works because it is an exception for default parameters and compilers might be built around that by parsing the checking the default parameters after parsing and "knowing" the whole class. You can read it in the link provided in the UPDATE section of my answer – Christian G Jun 09 '17 at 11:38

2 Answers2

33

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.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I like this answer because of another example and standard citation. If you are fast, I would choose this – ikh Jun 09 '17 at 13:03
  • 7
    @ikh You may always change your choice. But this demonstrates why it's a good idea to wait a while before accepting an answer in case a better one comes along. This is the correct answer. Speed and correctness are not always correlated. Accepting an incorrect answer creates confusion for people reading this page in the future. – Oktalist Jun 09 '17 at 13:48
0

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.

Arne Vogel
  • 6,346
  • 2
  • 18
  • 31