代码之家  ›  专栏  ›  技术社区  ›  llllllllll

具有模板专门化的constexpr数组成员:跨编译器的行为不一致

  •  6
  • llllllllll  · 技术社区  · 8 年前

    考虑以下代码:

    #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>::value foo<int>::value . 当我更改为 clang++ -std=c++17 ,则只有一个未定义的符号: foo<int>::价值 . 我的clang++版本是5.0。

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

    我有两件事要问。

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

    我已经在谷歌上搜索并阅读了许多cppreference页面,但没有找到任何与这一现象相关的内容。特别是对于带有 -std=c++17 ,这种行为真的很奇怪,因为 bar<int> 通过了,但是 foo<int> 失败,唯一的区别是 foo<内部(>); 是一种专业化。我从 http://en.cppreference.com/w/cpp/language/constexpr 那个

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

    因此,似乎没有理由进行模板专门化 foo<内部(>); 失败。此外,我在链接之前查看了生成的对象文件 foo<int>::value[2]; 按照预期在编译时完成。我高度怀疑clang++编译器中存在错误。

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

    我试过这样的 Undefined reference to static constexpr char[] ,但最后我找不到任何方法来克服这个链接错误。所以我只是想知道是否有一种方法可以让这种链接成功。

    1 回复  |  直到 5 年前
        1
  •  2
  •   xskxzr    8 年前

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

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


    [dcl.constexpr] paragraph 1

    ... 用声明的函数或静态数据成员 常量表达式 说明符隐式地是一个内联函数或变量([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 .

    这是一只叮当作响的虫子。不管怎样,它 works well 对于叮当头7.0.0。