tl;dr:Visual Studio 是对的——你不能这么说typedef
there.
Boost 文档明白这一点,但没有解释为什么 http://www.boost.org/doc/libs/1_54_0/doc/html/intrusive/usage.html#intrusive.usage.usage_member_hook.
[C++11: 14.3.2/1]:
A 模板参数对于非类型、非模板模板参数应为 one of:
- 对于非类型模板参数整型或枚举类型的转换后的常量表达式(5.19)模板参数; or
- 非类型的名称模板参数; or
- 常量表达式(5.19),指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID但不包括非静态类成员,表示(忽略括号)为
& id-expression
,除了&
如果名称引用函数或数组,则可以省略;如果对应的名称则应省略模板参数是一个参考;或者
- 计算结果为空指针值的常量表达式 (4.10);或者
- 计算结果为空成员指针值的常量表达式 (4.11);或者
-
指向成员的指针,如 5.3.1 中所述表示.
[C++11: 5.3.1/3]:
一元的结果&
运算符是指向其操作数的指针。操作数应为左值或合格的 ID。如果操作数是合格的 ID命名非静态成员m
某类的C
与类型T
,结果的类型为“指向类成员的指针”C
类型的T
” 并且是指定的纯右值C::m
. [..]
[C++11: 8.3.3/2]
给出了一个不完整类型的指向成员的指针的示例,只要该指向成员的指针未实际初始化,该指针就有效,尽管没有明确说明,但其含义是实际获取某个对象的地址C::m
, C
必须是一个完整的类型。确实,直到C
是一个完整的类型,C::m
并不真正存在。
有一些similar更清晰的规则:
[C++11: 9.2/10]
:非静态(9.4)数据成员不得具有不完整的类型。特别是一类C
不得包含类的非静态成员C
,但它可以包含对类对象的指针或引用C
.
在你的点typedef
, Test
不是一个完整的类型:
[C++11: 9.2/2]:
类在结束时被视为完全定义的对象类型 (3.9)(或完整类型)}
of the 类说明符。班级内会员规范,该类在函数体、默认参数内被认为是完整的,异常规范, and 大括号或等号初始化器对于非静态数据成员(包括嵌套类中的此类内容)。否则在自己的类中被认为是不完整的会员规范.
因此,您不能在该位置使用该指向成员的指针。您必须写下typedef
以便它在关闭后出现}
类定义的,或者使指向的对象成为非成员,或者static
member.
GCC 必须有这方面的错误或扩展 http://ideone.com/zl3H8o,因为以下测试用例编译并成功执行:
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> 测试.cpp
1>f:\documents\visual studio 2012\projects\test1\test1\test.cpp(8): 错误 C2327: 'B::x' : 不是类型名称、静态或枚举器
1>f:\documents\visual studio 2012\projects\test1\test1\test.cpp(8): 错误 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 跳过 ==========