Ошибка компоновщика при использовании класса шаблона со ссылочным параметром шаблона, отличным от типа

Я разрабатываю библиотеку C++, где мне нужно предоставить пользователю шаблон класса.

Параметр шаблона этого класса является ссылкой. Однако я получаю ошибку компоновщика. Вот минимальный тестовый пример:

тест.чч

#ifndef TEST_HH_
#define TEST_HH_

template <double const& val>
struct A{};

constexpr double zero = 0.;

A<zero> foo();

#endif // TEST_HH_

test.cpp

#include "test.hh"

A<zero> foo(){ return A<zero>(); }

main.cpp

#include "test.hh"

int main()
{
    foo();
}

При компиляции этого фрагмента кода я получаю предупреждение:

'A<zero> foo()' used but never defined

с последующей ошибкой компоновщика:

undefined reference to foo()

Я попытался заменить double на int :

template <int val>
struct A{};

и он связан (при передаче int в качестве параметра ofc), но мне действительно нужен double.

Я также пробовал обычное решение, когда класс шаблона включает ошибку компоновки, я реализовал функцию foo() в test.hh вместо test.cpp, но я хотел бы избежать простого размещения всего кода в заголовке.


person Baxlan    schedule 08.01.2019    source источник


Ответы (1)


Радуйся, что это не ссылка! Могло быть и хуже...

Проблема в основном в том, что test.cpp и main.cpp имеют разные объекты с именем ::zero. Это нарушение ODR — у вас есть несколько определений для этой переменной, и вы получаете A<zero>, технически являющиеся разными типами, поскольку параметр шаблона A val является ссылкой на разные объекты в разных единицах перевода.

Надеюсь, это объяснение проясняет, почему A<int> работает нормально.

В С++ 17 вы хотите:

inline constexpr double zero = 0.;

В C++14 вам нужно либо добавить определение в одну из единиц перевода, либо проделать какую-то другую магию, например, чтобы ::zero было ссылкой на какую-то статическую переменную шаблона или другое шаманство.

person Barry    schedule 08.01.2019
comment
О боже, это РАБОТАЕТ! Давайте перейдем на cpp 17 :) Большое спасибо. Я думал, что переменные в глобальной области видимости или в пространстве имен по умолчанию имеют внешнюю связь. (Да, это объясняет для int, потому что я не объявляю никаких переменных, я сделал A‹1› в обоих файлах, чтобы значения были одинаковыми). - person Baxlan; 09.01.2019
comment
@Baxlan У него есть внешняя связь - проблема не в этом. - person Barry; 09.01.2019
comment
Разве внешняя связь не означает, что переменная имеет только одно определение во всех единицах перевода? - person Baxlan; 09.01.2019
comment
@Baxlan, извините, я ошибся. Область пространства имен — это внутренняя связь. Это добавление inline делает его внешним. - person Barry; 09.01.2019