23

看看这段代码:

template <typename T, void (T::*pfn)()> struct Testee {};

class Tester
{
private:
    void foo() {}
public:
    using type_t = Testee<Tester, &Tester::foo>;    
};

它成功地用g++ -std=c++14 -Wall -Wextra.

foo但是,当我更改and的顺序时type_t,会发生错误:

$ 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>;
                                               ^

通常,类定义中的声明顺序对名称解析没有影响。例如:

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) { }
};

但是,它在这种情况下会产生影响。为什么?

4

2 回答 2

33

这与模板无关。您会收到类似的错误

class Tester
{
public:
    using type_t = decltype(&Tester::foo);
private:
    void foo() {}
};

确实,一个类是(标准 9.2/2):

在函数体、默认参数、引入继承构造函数(12.9)的使用声明、异常规范和 非静态数据成员(包括嵌套类中的此类内容)的大括号或等式初始化器中被视为完整。

但是,成员类型的定义不在该列表中,因此它只能使用在该点之前声明的名称。

于 2017-06-09T11:56:20.690 回答
0

通常,类定义中的声明顺序没有影响。

这真是夸大其词了。据我所知,允许在类定义后面出现一些声明:

  • 默认参数(如您所述;但不是默认模板参数)
  • 在函数体、函数尝试块或成员初始化程序中使用
  • 类内初始化器(C++11 或更高版本)

此外,如前所述,数据成员的顺序会影响构造和销毁顺序。此外,在翻译单元之间重新排序内容可能会令人惊讶地导致 ODR 违规。

于 2017-06-09T14:34:03.163 回答