5

我在 Windows 上面临“错误 C2327”。
我减少了我的代码并在测试程序中遇到了类似的错误

#include <boost/intrusive/list.hpp>
#include <iostream>

class Test {
protected:
         typedef Test self_type;
         boost::intrusive::list_member_hook<> order_hook;
public:
         typedef boost::intrusive::member_hook<self_type,
                            boost::intrusive::list_member_hook<>,
                            & Test::order_hook > order_hook_type;
};

这在 g++ 上运行良好,但在 Windows 上会出现以下错误:

test.cpp(11) : error C2327: 'Test::order_hook' : is not a type name, static, or enumerator
test.cpp(11) : error C2065: 'order_hook' : undeclared identifier

请帮忙。我在 Windows 中缺少什么?

4

3 回答 3

4

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,则结果具有类型“指向类型的成员的指针”并且是指定的纯右值。[..]mCTCTC::m

[C++11: 8.3.3/2]给出了一个不完整类型的指向成员的示例,只要指向成员的指针实际上没有被初始化,并且尽管没有明确说明,这意味着要实际获取 some 的地址C::mC必须是一个完整的类型。确实,直到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 个已跳过 ==========

于 2013-10-24T11:59:44.473 回答
3

tl;dr:Visual Studio 有一个错误:您的代码是合法的。


[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,则结果具有类型“指向类型的成员的指针”并且是指定的纯右值。[..]mCTCTC::m

[C++11: 3.4.3.1/1]: 如果限定id的嵌套名称说明符指定了一个类,则在类的范围内查找嵌套名称说明符之后指定的名称(10.2),但下面列出的情况除外。该名称应代表该类或其基类之一的一个或多个成员(第 10 条)。[注意:可以在其潜在范围 (3.3.7) 中的任何点使用限定 ID来引用类成员—尾注]上述名称查找规则的例外情况如下:

  • 按照 3.4.3 中的规定查找析构函数名称;
  • 以与类成员访问中的转换类型 ID相同的方式查找转换函数 ID的转换类型 ID (参见 3.4.5);
  • 在整个后缀表达式出现的上下文中查找模板 ID的模板参数中的名称。
  • 查找 using-declaration (7.3.3) 中指定的名称还可以找到隐藏在同一范围内的类或枚举名称 (3.3.10)。

这里没有任何例外,所以我们看一下类成员的“潜在范围”:

[C++11: 3.3.7/1]:以下规则描述了类中声明的名称范围。

  1. 类中声明的名称的潜在范围不仅包括名称声明点之后的声明区域,还包括所有函数体、非静态数据成员的大括号或等号初始化器以及其中的默认参数类(包括嵌套类中的此类内容)。
  2. [..]

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 个已跳过 ==========

于 2013-10-24T12:32:39.890 回答
0

改用 function_hook。基本上,这允许您在静态函数的主体中获取成员变量的地址,该函数可以在类 Test 的主体之外定义。

#include <boost/intrusive/list.hpp>
#include <boost/intrusive/parent_from_member.hpp>

class Test {
protected:
     boost::intrusive::list_member_hook<> order_hook;
private:
     struct hook_access
     {
         typedef boost::intrusive::list_member_hook<> hook_type;
         typedef hook_type* hook_ptr;
         typedef hook_type const* const_hook_ptr;
         typedef Test value_type;
         typedef value_type* pointer;
         typedef value_type const* const_pointer;

         static hook_ptr to_hook_ptr( value_type& i_value )
         { return & i_value.order_hook; }
         static const_hook_ptr to_hook_ptr( value_type const& i_value )
         { return & i_value.order_hook; }
         static pointer to_value_ptr( hook_ptr i_hook );
         static const_pointer to_value_ptr( const_hook_ptr i_hook );
     };
     friend struct hook_access; 
public:
     typedef boost::intrusive::function_hook< Test::hook_access > order_hook_type;
};

Test::hook_access::pointer 
Test::hook_access::to_value_ptr
( Test::hook_access::hook_ptr i_hook )
{ return boost::intrusive::get_parent_from_member< Test >( i_hook, & Test::order_hook ); }

Test::hook_access::const_pointer 
Test::hook_access::to_value_ptr
( Test::hook_access::const_hook_ptr i_hook )
{ return boost::intrusive::get_parent_from_member< Test >( i_hook, & Test::order_hook ); }
于 2018-01-13T22:04:10.783 回答