具有模板特化的constexpr数组成员:不一致的行为交叉编译器

时间:2018-01-20 22:45:02

标签: c++ c++14 constexpr clang++ c++17

请考虑以下代码:

#include <iostream>

template<class T>
struct foo {};

template<>
struct foo<int> {
  static constexpr char value[] = "abcde";
};

template<class T>
struct bar {
  static constexpr char value[] = "abcde";
};

template<class T>
struct baz {
  static constexpr int value = 12345;
};

int main() {
    char c = foo<int>::value[2];
    char d = bar<int>::value[2];
    int e = baz<int>::value;

    std::cout << c << d << e << "\n";

}

使用:clang++ -std=c++14 ./test_foo.cc进行编译时,我收到了未定义符号的链接器错误:bar<int>::valuefoo<int>::value。当我更改为clang++ -std=c++17时,只有一个未定义的符号:foo<int>::value。我的clang ++版本是5.0。

然而,当我尝试g++ -std=c++14 ./test_foo.cc时,编译成功。我的g ++版本是5.4.0。

我有两件事要问。

1)从C ++标准的角度来看,哪个编译器行为正确?

我用谷歌搜索并阅读了许多cppreference页面,但没有发现任何与此现象真正相关的内容。特别是对于使用-std=c++17的clang ++,行为非常奇怪,因为bar<int>已通过但foo<int>失败,唯一的区别是foo<int>是一个特化。我从http://en.cppreference.com/w/cpp/language/constexpr读到了

  

函数或静态成员变量(自C ++ 17)声明中使用的constexpr说明符暗示内联。

因此,似乎没有理由让模板特化foo<int>失败。此外,我在链接之前查看了生成的目标文件,在编译时对foo<int>::value[2];的访问就像人们期望的那样。我非常怀疑clang ++编译器有什么问题。

2)如何处理这个clang ++链接错误?

我尝试过类似Undefined reference to static constexpr char[]的内容,但最后我无法找到任何方法来克服此链接错误。所以我只是想知道是否有办法让这种连接成功。

1 个答案:

答案 0 :(得分:2)

直到C ++ 17,其原因与the question you found中所述的完全相同(我认为Shafik Yaghmour发布的答案更准确地解释了这个问题)。简而言之,如果成员是odr-used,则仍然需要定义constexpr静态数据成员。

您可以resolve the problem提供这些变量的定义,直到C ++ 17 (即使用-std=c++14)。

自C ++ 17以来,[dcl.constexpr] paragraph 1中的当前标准说

  

...使用constexpr说明符声明的函数或静态数据成员隐式地是内联函数或变量([dcl.inline])。

并在[basic.def] paragraph 2中说

  

声明是定义,除非

     
      
  • ...

  •   
  • 它在类定义中声明了非内联静态数据成员([class.mem],[class.static]),

  •   
  • ...

  •   

因此,不需要在命名空间范围内进行此类定义。

此外,[depr.static_constexpr] paragraph 1中的当前标准是

  

为了与先前的C ++国际标准兼容,constexpr静态数据成员可以在课外冗余重新声明而不使用初始化程序。不推荐使用此用法。 [实施例:

struct A {
  static constexpr int n = 5;  // definition (declaration in C++ 2014)
};

constexpr int A::n;  // redundant declaration (definition in C++ 2014)
     

- 结束示例]

因此,从C ++ 17开始,你最好避免这种定义。

  

当我更改为clang++ -std=c++17时,只有一个未定义的符号:foo<int>::value

这是一个Clang错误。无论如何,对于Clang HEAD 7.0.0它works well