为什么声明顺序对于将成员函数指针作为模板参数传递很重要?

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 可以成功编译。

但是,当我改变 footype_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) { }
};

然而,在这种情况下它有影响。为什么?

你尝试过使用不同的编译器吗? - max
5
据我所知,“类定义中声明的顺序没有影响”这句话并不完全正确,例如,成员变量是按照它们声明的顺序进行构造的。 - 463035818_is_not_a_number
在这种情况下,没有构造函数被调用(即根本没有调用ctor)。 - Luca Cappa
@LucaCappa 我知道,我只是说一般情况下这个陈述不正确。 - 463035818_is_not_a_number
你的默认参数示例之所以有效,是因为它是默认参数的一个特例,编译器可能通过解析并在解析后检查默认参数,并“知道”整个类来构建编译器。你可以在我的回答的UPDATE部分提供的链接中阅读相关信息。 - Christian G
显示剩余2条评论
2个回答

33

这与模板没有太大关系。你会在此处遇到类似的错误:

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

确实,根据标准9.2/2,类在函数体、默认参数、引入继承构造函数的using声明(12.9)、异常规格说明和非静态数据成员的花括号或等号初始化器(包括嵌套类中的这些内容)中被视为完整。

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


我喜欢这个答案,因为它提供了另一个例子和标准引用。如果你速度快的话,我会选择这个。 - ikh
7
@ikh,你始终可以更改你的选择。但这表明在接受答案之前等待一段时间以便有更好的答案出现是一个好主意。这是正确的答案。速度和正确性并不总是相关的。接受一个错误的答案会给将来阅读本页的人带来困惑。 - Oktalist

0
通常,在类定义中声明的顺序没有影响,这是一个相当夸张的说法。
据我所知,类定义中后面出现的一些声明是允许的,例如:
  • 默认参数(正如您所提到的;但不包括默认模板参数)
  • 函数体、函数尝试块或成员初始化器中的使用
  • 类内初始化器(C++11或更高版本)
此外,正如已经提到的,数据成员的顺序会影响构造和销毁顺序。此外,在翻译单元之间重新排序可能会导致意外的ODR违规。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接