tl;dr:Visual Studio 是对的——你不能把它放在typedef
那里。
Boost 文档是正确的,但没有解释为什么.
[C++11: 14.3.2/1]:
非类型、非模板模板参数的模板参数应为以下之一:
- 对于整数或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或者
- 非类型模板参数的名称;或者
- 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号) as
& id-expression
,但&
如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或者
- 计算结果为空指针值的常量表达式(4.10);或者
- 一个常量表达式,计算结果为空成员指针值 (4.11);或者
- 指向成员的指针,如 5.3.1 中所述。
[C++11: 5.3.1/3]:
一元运算符的结果&
是指向其操作数的指针。操作数应该是一个左值或一个qualified-id。如果操作数是用 type命名某个类的非静态成员的限定 ID,则结果具有类型“指向类型的成员的指针”并且是指定的纯右值。[..]m
C
T
C
T
C::m
[C++11: 8.3.3/2]
给出了一个不完整类型的指向成员的示例,只要指向成员的指针实际上没有被初始化,并且尽管没有明确说明,这意味着要实际获取 some 的地址C::m
,C
必须是一个完整的类型。确实,直到C
是一个完整的类型,C::m
并不真正存在。
有一些类似的规则更清晰:
[C++11: 9.2/10]
:非静态(9.4)数据成员不应有不完整的类型。特别是,类C
不应包含类的非静态成员C
,但可以包含指向类对象的指针或引用C
。
在你的typedef
,Test
不是一个完整的类型:
[C++11: 9.2/2]:
}
在class-specifier结束时,类被视为完全定义的对象类型 (3.9)(或完整类型)。在类成员规范中,类在函数体、默认参数、异常规范和非静态数据成员(包括嵌套类中的此类内容)的大括号或等式初始化器中被视为完整。否则,它在其自己的类成员规范中被视为不完整。
因此,您不能在该位置使用指向成员的指针。您必须编写typedef
以便它}
在类定义结束后出现,或者使指向的对象成为非成员或static
成员。
GCC必须在这方面存在错误或扩展,因为以下测试用例编译并成功执行:
template <typename B, int B::* PTM>
struct A {};
struct B
{
int x;
typedef A<B, &B::x> a;
};
int main() {
B b;
}
而 Visual Studio 2012 Express 正确输出:
1>----- 构建开始:项目:test1,配置:调试 Win32 ------
1> test.cpp
1>f:\documents\visual studio 2012\projects\test1\test1\test.cpp (8): error C2327: 'B::x' : is not a type name, static, or enumerator
1>f:\documents\visual studio 2012\projects\test1\test1\test.cpp(8): error C2065 :'x':未声明的标识符
1>f:\documents\visual studio 2012\projects\test1\test1\test.cpp(8):错误 C2975:'PTM':'A' 的模板参数无效,预期编译时间常量表达式
1> f:\documents\visual studio 2012\projects\test1\test1\test.cpp(1) :参见“PTM”声明
========== 构建:0 成功,1 失败, 0 个最新,0 个已跳过 ==========