错误 C2327:不是类型名称、静态或枚举器

error C2327: not a type name, static, or enumerator

本文关键字:静态 枚举 C2327 类型 错误      更新时间:2023-10-16

我在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

请帮忙。我缺少什么窗户?

tl;dr:Visual Studio是对的——你不能把这个typedef放在那里。Boost 文档对此表示正确,但没有解释原因。

<小时 />

[C++11: 14.3.2/1]: 非类型、非模板模板参数的模板参数应为以下参数之一:

  • 对于整型或枚举类型的非类型模板参数,模板参数类型的转换常量表达式 (5.19);或
  • 非类型模板参数的名称;或
  • 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号)为 & id-expression ,但如果名称引用函数或数组,则可以省略&,如果相应的模板参数,则应省略是参考;或
  • 计算结果为 null 指针值 (4.10) 的常量表达式;或
  • 计算结果为 null 成员指针值 (4.11) 的常量表达式;或
  • 指向成员的指针,如 5.3.1 中所述

[C++11: 5.3.1/3]: 一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定 ID。如果操作数是一个限定 id,命名某个类m C T的非静态成员,则结果的类型为"指向类型 T 的类 C 成员的指针",并且是指定C::m的 prvalue 。[..]

[C++11: 8.3.3/2]给出了一个示例,说明一个不完整类型的指针到成员,只要指针到成员没有实际初始化,它就有效,虽然没有明确说明,但这意味着要实际获取某些C::m的地址,C必须是完整的类型。事实上,在C成为一个完整的类型之前,C::m并不真正存在。

有一些类似的规则更清楚:

[C++11: 9.2/10]:非静态 (9.4) 数据成员不得具有不完整的类型。特别是,类C不应包含类 C 的非静态成员,但它可以包含指向类 C 对象的指针或引用。

typedef时,Test不是一个完整的类型:

[C++11: 9.2/2]: 类在类说明符的结束}被视为完全定义的对象类型 (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>测试.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 跳过 ==========

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


[C++11: 14.3.2/1]: 非类型、非模板模板参数的模板参数应为以下参数之一:

  • 对于整型或枚举类型的非类型模板参数,模板参数类型的转换常量表达式 (5.19);或
  • 非类型模板参数的名称;或
  • 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号)为 & id-expression ,但如果名称引用函数或数组,则可以省略&,如果相应的模板参数,则应省略是参考;或
  • 计算结果为 null 指针值 (4.10) 的常量表达式;或
  • 计算结果为 null 成员指针值 (4.11) 的常量表达式;或
  • 指向成员的指针,如 5.3.1 中所述

[C++11: 5.3.1/3]: 一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定 ID。如果操作数是一个限定 id,命名某个类m非静态成员C类型为 T,则结果的类型为"指向类型 T 的类 C 成员的指针",并且是指定C::m的 prvalue 。[..]

[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-声明 (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>测试.cpp
1>f:\documents\Visual Studio 2012\项目\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 跳过 ==========

改用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 ); }