`static constexpr auto` data-member用未命名的枚举初始化

时间:2014-06-03 15:20:28

标签: c++ c++11 enums language-lawyer auto

我正在使用clang++-3.4开发一个C ++ 11项目,并决定使用g++-4.8.2进行编译,以防产生的错误有任何差异。事实证明,g ++拒绝了一些clang ++接受的代码。我已将问题减少到下面给出的MWE。


enum { a };

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

int main()
{
    static constexpr auto r = foo<int>::value;
}

  

foo.cpp:5:23:错误:使用匿名类型声明的'const<anonymous enum> foo<int>::value',但从未定义[-fpermissive]

static const auto value = A;

我想帮助回答以下两个问题:

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

  • 如何解决此问题?我无法为匿名枚举命名,因为它来自第三方库(在我的情况下,枚举为Eigen::RowMajorEigen::ColMajor)。

3 个答案:

答案 0 :(得分:14)

谁应该受到指责?

GCC 不准确地拒绝您的代码段,根据 C ++ 11 标准(N3337),它是合法的。带有证据和解释的报价位于本文末尾。

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

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;


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

#include <type_traits>

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

标准说什么? (N3337

如上所述,该代码段是合法的 C ++ 11 ,可以在以下引用部分中阅读。


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

[basic.link]p8有详细的措辞,用于描述类型何时&#34;没有链接&#34; ,并且它声明未命名的枚举计数为此类型。

[basic.link]p8还明确说明了三种不能使用这种类型的上下文,但其中一种上下文不适用于我们的使用,所以我们是安全的。

  
    

除非

,否则不带链接的类型不能用作具有外部链接的变量或函数的类型          
        
  • 该实体具有C语言链接(7.5)或
  •     
  • 实体在未命名的命名空间(7.3.1)或
  • 中声明     
  • 实体不是odr-used(3.2)或在同一翻译单元中定义
  •     
  


您确定我们可以在此类背景下使用auto吗?

是的,这可以通过以下引用证明:

  

7.1.6.4p auto说明符 [dcl.spec.auto]

     
    

类型说明符也可用于在中选择语句(6.4)或迭代语句(6.5)的条件中声明变量 new-expression new-type-id type-id 中的type-specifier-seq (5.3.4 ),在 for-range-declaration 中,以及在成员规范中出现的 brace-or-equal-initializer 声明静态数据成员类定义(9.4.2)。

  

答案 1 :(得分:10)

  

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

gcc不正确。 §9.4.2/ 3:

  

可以在类中声明文字类型的静态数据成员   使用constexpr说明符定义;如果是的话,其声明应   指定一个brace-or-equal-initializer,其中包含每个initializer子句   这是一个赋值表达式是一个常量表达式。 会员   仍然应在命名空间范围中定义,如果它是使用了 (3.2)in   程序和命名空间范围定义不应包含   初始化程序。

根据§3.2的说法,这个名字并没有被使用:

  

名称显示为可能评估的表达式的变量是    odr-used 除非它是满足出现在常量表达式(5.19)和左值到右值的要求的对象   转换(4.1)立即应用

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


可能的解决方法是定义成员(但没有占位符类型)。这个定义对于Clang和GCC都是足够的:

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

答案 2 :(得分:5)

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;
}