I'm failing to understand a linker error in C++14 with gcc
.
My purpose was to have a template that exposes a name, according to a non-type template parameter (a kind of compile-time mapping between a value and a string).
Lacking support for constexpr
string in C++14. I implemented a very basic wrapper around a static plain C string:
In header.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
For simplicity, here the string is always the same.
Then I've got a translation unit where I'm using this:
In test.cpp
void test() {
// injecting my overloaded operator<<
using nConstStr::operator<<;
std::cout << dummy<42>::name << '\n';
}
Which I call from main
.
When compiling with gcc in C++14 in a debug configuration, I've got the link error:
src.cpp:8: undefined reference to `dummy<42ul>::name'
Live.
On my machine, yet, it compiles and runs fine in -O2
.
It also works fine with C++17 or more.
Note, MSVC
accepts the code but clang
seems consistent with gcc
(at least on godbolt, couldn't test locally).
Prior to C++17 (in which you can declare the static
members inline
and static constexpr
members will be implicitly inline
) you need to provide an out-of-class definition too:
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;
Note: Since you define the operator<<
overload in the header file, it should be declared 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;
}