C++ `静态constexpr auto`使用未命名枚举初始化的数据成员

C++ `静态constexpr auto`使用未命名枚举初始化的数据成员,c++,c++11,enums,language-lawyer,auto,C++,C++11,Enums,Language Lawyer,Auto,我当时正在使用clang++-3.4编写一个C++11项目,并决定使用g++-4.8.2进行编译,以防产生的错误有任何差异。结果是g++拒绝了一些clang++接受的代码。我已将问题简化为下面给出的MWE 我想得到一些帮助来回答以下两个问题: 哪个编译器对标准的解释是正确的?我假设一个编译器在接受或拒绝代码方面是正确的,而另一个是错误的 我如何解决这个问题?我无法命名匿名枚举,因为它来自第三方库(在我的例子中,枚举是Eigen::rowmear和Eigen::ColMajor) 使用d

我当时正在使用
clang++-3.4
编写一个C++11项目,并决定使用
g++-4.8.2
进行编译,以防产生的错误有任何差异。结果是g++拒绝了一些clang++接受的代码。我已将问题简化为下面给出的MWE



我想得到一些帮助来回答以下两个问题:

  • 哪个编译器对标准的解释是正确的?我假设一个编译器在接受或拒绝代码方面是正确的,而另一个是错误的

  • 我如何解决这个问题?我无法命名匿名枚举,因为它来自第三方库(在我的例子中,枚举是
    Eigen::rowmear
    Eigen::ColMajor


使用
decltype
的变通方法:

enum { a };

template <class T>
struct foo
{
    static constexpr auto value = a;
};

template <class T>
constexpr decltype(a) foo<T>::value;

int main()
{
    static constexpr auto r = foo<int>::value;
}
enum{a};
模板
结构foo
{
静态constexpr自动值=a;
};
模板
constexpr decltype(a)foo::value;
int main()
{
静态constexpr auto r=foo::value;
}
该怪谁? GCC错误地拒绝了您的代码片段,根据C++11标准(N3337),这是合法的。带证明和解释的引文位于本帖末尾

解决方法(A)-添加缺少的定义

模板
结构foo{
静态constexpr自动值=a;
typedef decltype(a)值_类型;
};
模板
constexpr typename foo::value\u type foo::value;

解决方法(B)-使用枚举的基础类型作为占位符

#包括
模板
结构foo{
静态常量std::底层类型::类型值=a;
};

标准怎么说?() 如上所述,该代码段是合法的C++11,可以在下面引用的部分中阅读


什么时候可以使用没有链接的类型?

[basic.link]p8
有详细的措辞,描述了一个类型何时为“无链接”,并声明一个未命名的枚举计数为这种类型

[basic.link]p8
还明确指出了三种不能使用此类类型的上下文,但没有一种上下文适用于我们的使用,因此我们是安全的

不带联动装置的类型不得用作带有外部联动装置的变量或函数类型,除非

  • 实体具有C语言链接(7.5),或
  • 实体在未命名命名空间(7.3.1)中声明,或
  • 实体不是(3.2)或定义在同一翻译单位中

您确定我们可以在这种情况下使用
auto
? 是的,这可以通过以下报价证明:

7.1.6.4p
auto
specifier
[dcl.spec.auto]

auto
类型说明符也可用于在选择语句(6.4)或迭代语句(6.5)的条件下声明变量,在新类型id或新表达式(5.3.4)的类型说明符seq中,在for范围声明中,以及在声明静态数据成员时使用大括号或相等的初始值设定项,该初始值设定项出现在类定义的成员规范中(9.4.2)

哪个编译器对标准的解释是正确的

gcc
不正确。§9.4.2/3:

可以在类中声明文本类型的静态数据成员 使用constexpr说明符定义;如有,其声明应 指定一个大括号或相等的初始值设定项,其中包含每个初始值设定项子句 这是一个赋值表达式,它是一个常量表达式成员 如果在中使用odr(3.2),则仍应在命名空间范围中定义 程序和命名空间范围定义不应包含 初始化器

且名称未按照§3.2的规定使用:

名称显示为潜在计算表达式的变量为 odr使用,除非它是一个满足出现在常量表达式(5.19)中的要求以及从左值到右值的对象 立即应用转换(4.1)

事实确实如此:它确实满足出现在常量表达式中的要求,并且立即应用从左值到右值的转换(它用作对象的初始值设定项)。所以GCC的拒绝是不正确的


一种可能的解决方法是定义成员(但不使用占位符类型)。此定义对于Clang和GCC都足够:

template< typename T >
constexpr decltype(a) foo<T>::value;
模板
constexpr decltype(a)foo::value;

您是否定义了变量?或者错误信息是什么?@Arcoth我认为他没有提供定义,如果你提供了,错误就会消失。错误消息。我认为问题在于引用
foo::value
是否构成odr使用。gcc似乎认为是,而clang认为不是。@Praetorian是,对不起,我愚蠢地忘记了包含实际的错误消息应该作为一种解决方法,但最新的clang拒绝了它。目前正在调查它是否是一个bug。感谢您提供全面的答案和解决方法。您是否已在GCC中提交了原始错误的报告?如果你愿意,你可以去做。否则,我可以获得荣誉。老实说,我正要去做,你想写一篇吗?若有,;这都是你的。如果你愿意,你可以做;这完全取决于你。我只是想提供一些建议,以防您希望其他人这样做。@FilipRoséen refp我不确定
[basic.link]p8
在这里是否真的相关。它说“没有链接的类型不得用作变量或函数的类型…”,标准中提到枚举器名称是变量的地方?
enum { a };

template <class T>
struct foo
{
    static constexpr auto value = a;
};

template <class T>
constexpr decltype(a) foo<T>::value;

int main()
{
    static constexpr auto r = foo<int>::value;
}
template <class T>
struct foo {
    static constexpr auto value = a;
    typedef decltype(a) value_type;
};

template<class T>
constexpr typename foo<T>::value_type foo<T>::value;
#include <type_traits>

template <class T>
struct foo {
  static const std::underlying_type<decltype(a)>::type value = a;
};
template< typename T >
constexpr decltype(a) foo<T>::value;