在调试配置中使用gcc初始化静态constexpr自制字符串变量时未定义的符号,C++14 [重复]

plicqrtu  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(104)

此问题在此处已有答案

Undefined reference to static constexpr char[](6个答案)
Undefined reference error for static constexpr member(2个答案)
static constexpr undefined reference error on clang(3个答案)
2天前关闭。
我无法理解C14中gcc的链接器错误。
我的目的是根据非类型模板参数(一种值和字符串之间的编译时Map),创建一个公开名称的模板。
在C
14中缺少对constexpr字符串的支持。我实现了一个非常基本的围绕静态普通C字符串的 Package 器:
在信头. h中

#ifndef HEADER
#define HEADER
#include <cstddef>
#include <ostream>

namespace nConstStr
{
class constStr
{
public:
    constexpr constStr() noexcept = default;
    constexpr constStr(constStr const& str) noexcept :
        _str(str._str), _sz(str._sz)
    {
    }
    template<std::size_t N>
    constexpr explicit constStr(char const (&str)[N]) noexcept :
        _str(str), _sz(N - 1)
    {
    }
    ~constStr() noexcept = default;
    constexpr char operator[](std::size_t const i) const noexcept
    {
        return _str[i];
    }
    constexpr std::size_t size() const noexcept
    {
        return _sz;
    }

private:
    char const* _str = nullptr;
    std::size_t _sz = 0;
};
std::ostream& operator<<(std::ostream& os, constStr const& str)
{
    for (std::size_t i = 0; i < str.size(); ++i)
    {
        os << str[i];
    }
    return os;
}
}

template<std::size_t I> class dummy final
{
public:
    static constexpr nConstStr::constStr name = nConstStr::constStr{"dummy"};
};

#endif

字符串
为了简单起见,这里的字符串总是相同的。然后我有一个翻译单元,我在这里使用:
在test.cpp中

void test() {
    // injecting my overloaded operator<<
    using nConstStr::operator<<;
    std::cout << dummy<42>::name << '\n';
}


我从main调用它。
在调试配置中使用C14中的gcc进行编译时,我遇到了链接错误:
src.cpp:8:未定义对'dummy<42ul>::name'的引用
Live的函数。
然而,在我的机器上,它在-O2中编译和运行得很好。
它在C
17或更高版本中也能正常工作。
请注意,MSVC接受代码,但clang似乎与gcc一致(至少在godbolt上是这样,无法在本地进行测试)。

7uhlpewt

7uhlpewt1#

问题是,在C17之前,我们需要为constexpr static数据成员提供一个类外定义。从C17开始,类外定义是可选的。这就是为什么,当你用C17尝试你的程序时,它开始工作,没有任何链接器错误。
static可以看出:
constexpr static数据成员是隐式内联的,不需要在命名空间范围内重新声明。这种不带初始化器的重新声明(以前是必需的)仍然是允许的,但不推荐使用。(since C++17)
(强调我的)
这意味着要在C
14中解决这个问题,您需要在头文件header.h中为constexpr static成员添加以下类外定义。

header.h

//this is needed prior to C++17 but optional in C++17
template<std::size_t I> constexpr nConstStr::constStr dummy<I>:: name;

字符串
Working demo

vnjpjtjt

vnjpjtjt2#

在C17之前(在C17中,你可以声明static成员inlinestatic constexpr成员将隐式地是inline),你还需要提供一个类外定义:

template<std::size_t I>
class dummy final {
public:
    static constexpr nConstStr::constStr name = nConstStr::constStr{"dummy"};
};

// Add this:
template<std::size_t I>
constexpr nConstStr::constStr dummy<I>::name;

字符串
注意:由于你在头文件中定义了operator<<重载,它应该被声明为inline

inline std::ostream& operator<<(std::ostream& os, constStr const& str) {
^^^^^^
    for(std::size_t i = 0; i < str.size(); ++i) {
        os << str[i];
    }
    return os;
}

相关问题