Here's about the most minimal example I can produce that displays this behaviour:
foo.h
#pragma once
#include <string>
template <int I>
struct foo
{
constexpr explicit foo(int k)
: j(k + I)
{ }
std::string to_string() const;
private:
int j;
};
extern template struct foo<0>;
constexpr foo<0> operator"" _foo0(unsigned long long int v)
{
return foo<0>(static_cast<int>(v));
}
foo.cpp
#include "foo.h"
template <int I>
std::string foo<I>::to_string() const
{
return std::to_string(j);
}
template struct foo<0>;
bar.h
#pragma once
#include "foo.h"
#include <vector>
template <typename T>
struct bar
{
explicit bar(int i);
private:
std::vector<T> vec;
};
extern template struct bar<foo<0>>;
bar.cpp
#include "bar.h"
template <typename T>
bar<T>::bar(int i)
{
vec.push_back(T{i});
}
template struct bar<foo<0>>;
This is used as follows:
main.cpp
#include "bar.h"
int main()
{
bar<foo<0>> b2(5);
}
Compiling this as under GCC (I've tried both 7.4.0 and 8.3.0):
g++-8 foo.cpp bar.cpp main.cpp -std=c++14 -Wall -Werror -o test
Gives the error:
bar.cpp:(.text._ZN3barI3fooILi0EEEC2Ei[_ZN3barI3fooILi0EEEC5Ei]+0x3c): undefined reference to `foo<0>::foo(int)'
Clang versions 4 to 7 seem to accept this.
Two small changes make GCC accept it:
- Removing the
constexpr operator""definition, or - Changing both the
operator""and thefooconstructor toinlineinstead ofconstexpr.
Is this legal, and does GCC have a valid reason for rejecting this as-is?
-std=c++17. - Meowmere