Вот самый минимальный пример, который я могу привести, который отображает это поведение:
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>>;
Используется следующим образом:
main.cpp
#include "bar.h"
int main()
{
bar<foo<0>> b2(5);
}
Компиляция как в GCC (я пробовал 7.4.0 и 8.3.0):
g++-8 foo.cpp bar.cpp main.cpp -std=c++14 -Wall -Werror -o test
Выдает ошибку:
bar.cpp :(. Text._ZN3barI3fooILi0EEEC2Ei [_ZN3barI3fooILi0EEEC5Ei] + 0x3c): неопределенная ссылка на `foo <0> :: foo (int) '
Clang версии от 4 до 7, кажется, принимают это.
Два небольших изменения заставляют GCC принять это:
- Удаление определения
constexpr operator""
или - Изменение конструкторов
operator""
и foo
на inline
вместо constexpr
.
Является ли это законным, и есть ли у GCC веская причина для отклонения этого как-is