【问题标题】:constexpr of static tuple class member has linker error静态元组类成员的 constexpr 有链接器错误
【发布时间】:2015-01-25 08:50:25
【问题描述】:

我有以下代码:

#include <iostream>
#include <tuple>

class T
{
    public:
        using Names = std::tuple<char const*, char const*>;
        static constexpr Names names {"First", "Second"};
};

int main()
{
    std::cout << std::get<0>(T::names);
}

因为namesconstexpr,所以我希望这可以工作。但我得到一个链接器错误:

编译器:

> g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 6.0 (clang-600.0.56) (based on LLVM 3.5svn)
Target: x86_64-apple-darwin14.0.0
Thread model: posix

错误:

> g++ -std=c++1y pl.cpp
Undefined symbols for architecture x86_64:
  "T::names", referenced from:
      _main in pl-377031.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

[live demo]

【问题讨论】:

    标签: c++ c++11 constexpr


    【解决方案1】:

    类中static 数据成员的声明绝不是定义1
    每当变量被 odr-used2 时,定义都是必要的。 std::get&lt;&gt; 接受参数每个引用,并将变量绑定到引用 odr-立即使用它3

    在外面简单定义names

    constexpr T::Names T::names; // Edit: This goes *outside* the class "as is"!
    

    Demo.


    1) [basic.def]/2:

    声明是一个定义,除非 [..] 它声明了 static 数据 类定义中的成员(9.2、9.4)

    2) [basic.def.odr]/4:

    每个程序都应该包含每个非内联的定义 在该程序中使用 odr 的函数或变量;没有诊断 必填。

    3)根据[basic.def.odr]/3:

    变量x,其名称显示为潜在求值表达式 exex 使用,除非应用左值到右值的转换 (4.1) 到 x 产生一个不调用的常量表达式 (5.19) 任何重要的函数,如果x 是一个对象,ex 是一个元素 表达式e 的一组潜在结果,其中 左值到右值转换 (4.1) 应用于 e,或者 e 是 丢弃值表达式(第 5 条)。

    这里的 id 表达式 T::names 指的是有问题的变量。包含T::names 的所有潜在结果的唯一超表达式eT::names 本身,因为函数调用的潜在结果集,即std::get&lt;0&gt;(T::names),是空的。但是,左值到右值的转换显然没有应用,T::names 的值也显然没有被丢弃(因为它被传递给函数)。
    因此它是 odr-used 并且需要定义。

    【讨论】:

    • 我假设constexpr T::Names T::names;必须在一个编译单元中,不能在一个头文件中多次包含。
    • @CrappyExperienceBye 正确 - IIRC T::names 具有外部链接。
    • 由于涉及的术语,我很难理解 odr-use:我已经阅读了标准和 cppreference 中的潜在评估表达式、左值到右值转换以及潜在结果,但是这令人困惑。是否有全面的资源可以用英语解释 odr-use?
    【解决方案2】:

    @Columbo 发布了正确的解决方案。

    不幸的是,我正在尝试构建一个仅标头库。该解决方案要求将静态成员编译到一个编译单元中(这是我使用constexpr 希望避免的)。

    所以我需要在作品中加入另一个转折点以使其发挥作用。这只是为了分享我的解决方案:

    #include <iostream>
    
    class T
    {
        public:
            using Names = std::tuple<char const*, char const*>;
            template<std::size_t index>
            static char const* getName()
            {
                static constexpr Names names {"First", "Second"};
                return std::get<index>(names);
            }
    };
    
    int main()
    {
        std::cout << T::getName<0>() << "\n";
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-17
      • 2018-11-06
      • 2014-04-06
      • 2015-10-31
      • 1970-01-01
      • 1970-01-01
      • 2019-08-02
      相关资源
      最近更新 更多