此问题在此处已有答案:
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),创建一个公开名称的模板。
在C14中缺少对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
中编译和运行得很好。
它在C17或更高版本中也能正常工作。
请注意,MSVC
接受代码,但clang
似乎与gcc
一致(至少在godbolt上是这样,无法在本地进行测试)。
2条答案
按热度按时间7uhlpewt1#
问题是,在C17之前,我们需要为
constexpr static
数据成员提供一个类外定义。从C17开始,类外定义是可选的。这就是为什么,当你用C17尝试你的程序时,它开始工作,没有任何链接器错误。从static可以看出:
constexpr static
数据成员是隐式内联的,不需要在命名空间范围内重新声明。这种不带初始化器的重新声明(以前是必需的)仍然是允许的,但不推荐使用。(since C++17)
(强调我的)
这意味着要在C14中解决这个问题,您需要在头文件
header.h
中为constexpr static
成员添加以下类外定义。header.h
字符串
Working demo的
vnjpjtjt2#
在C17之前(在C17中,你可以声明
static
成员inline
和static constexpr
成员将隐式地是inline
),你还需要提供一个类外定义:字符串
注意:由于你在头文件中定义了
operator<<
重载,它应该被声明为inline
:型