为什么声明顺序对于将成员函数指针作为模板参数传递很重要?
Why is declaration order important for passing a member function pointer as a template argument?
看这段代码:
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
和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>;
^
通常,class 定义中的声明顺序对名称解析没有影响。例如:
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) { }
};
不过,在这种情况下是有效果的。为什么?
这与模板无关。您在
上获得了类似的 error
class Tester
{
public:
using type_t = decltype(&Tester::foo);
private:
void foo() {}
};
确实class是(标准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).
但是,成员类型的定义不在该列表中,因此它只能使用在该点之前声明的名称。
Usually, the order of declaration in class definition have no effects.
这太夸张了。据我所知,class 定义中稍后出现的一些声明是允许的:
- 默认参数(如您所述;但不是默认模板参数)
- 在函数体、函数尝试块或成员初始值设定项中使用
- in-class 初始化器(C++11 或更高版本)
此外,如前所述,数据成员的顺序会影响构造和销毁顺序。此外,在翻译单元之间重新排序内容可能会令人惊讶地导致 ODR 违规。
看这段代码:
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
和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>;
^
通常,class 定义中的声明顺序对名称解析没有影响。例如:
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) { }
};
不过,在这种情况下是有效果的。为什么?
这与模板无关。您在
上获得了类似的 errorclass Tester
{
public:
using type_t = decltype(&Tester::foo);
private:
void foo() {}
};
确实class是(标准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).
但是,成员类型的定义不在该列表中,因此它只能使用在该点之前声明的名称。
Usually, the order of declaration in class definition have no effects.
这太夸张了。据我所知,class 定义中稍后出现的一些声明是允许的:
- 默认参数(如您所述;但不是默认模板参数)
- 在函数体、函数尝试块或成员初始值设定项中使用
- in-class 初始化器(C++11 或更高版本)
此外,如前所述,数据成员的顺序会影响构造和销毁顺序。此外,在翻译单元之间重新排序内容可能会令人惊讶地导致 ODR 违规。