Нарушение ODR со специализациями шаблона

У нас есть заголовочный файл, который содержит некоторые остатки для различных значений точности с плавающей запятой:

template <typename T>
struct rsdTarget {
  static const double value;
};

template <>
const double rsdTarget<half>::value = (double)(1.0e-3);

template <>
const double rsdTarget<float>::value = (double)(1.0e-7);

template <>
const double rsdTarget<double>::value = (double)(1.0e-12);

Это сработало, потому что этот заголовок был включен только в одну единицу компиляции. Теперь я пытаюсь использовать этот заголовок в нескольких модулях компиляции и получаю ошибки компоновщика, связанные с ODR:

CMakeFiles/tests_g.dir/random_gauge.cc.o:(.rodata+0x108): multiple definition of `rsdTarget<double>::value'
CMakeFiles/tests_g.dir/clover_product.cc.o:(.rodata+0x548): first defined here

Инициализация, вероятно, должна войти в исходный файл и быть извлечена из файла заголовка. Однако кажется, что extern перед const double запрещено.

Что мне нужно сделать, чтобы это работало с несколькими единицами компиляции?

Обновлять

Я думал, что решение этой проблемы для double полностью исправит ситуацию. Однако есть второй небуквенный тип, который я также должен выполнить:

template <typename T>
struct tolerance {
  static const QDP::Double small; // Always fail
};

template <>
const QDP::Double tolerance<half>::small = QDP::Double(5.0e-3);

template <>
const QDP::Double tolerance<float>::small = QDP::Double(1.0e-6);

template <>
const QDP::Double tolerance<double>::small = QDP::Double(1.0e-7);

Кажется, я не могу использовать это с constexpr, потому что этот тип его не поддерживает (требуется constexpr ctor, верно?). Какое из решений работает и с этим?


person Martin Ueding    schedule 20.07.2017    source источник
comment
Используйте inline в специализации. (И рассмотрите возможность замены этого встроенной переменной, если вы используете современный компилятор)   -  person M.M    schedule 20.07.2017
comment
Вы также можете использовать __declspec(selectany) перед специализацией шаблона для компилятора Microsoft.   -  person ikleschenkov    schedule 20.07.2017
comment
Встроенные переменные, похоже, нуждаются в С++ 17, сейчас мы только до С++ 11. Это высокопроизводительный код, который необходимо запускать на различных системах HPC с непредсказуемыми версиями компилятора. Попробую поиграться с инлайном.   -  person Martin Ueding    schedule 20.07.2017
comment
@ikleschenkov: я не вижу, чтобы мы когда-либо использовали компилятор Microsoft. Мы используем GCC, Clang, IBM XLC (но не для этого кода), Intel C++ и Cray Compiler. Наши целевые системы работают на Linux :-).   -  person Martin Ueding    schedule 20.07.2017
comment
Я обновил вопросы, потому что проблема оказалась более неприятной, чем я думал. Прости за это!   -  person Martin Ueding    schedule 20.07.2017
comment
Решение VTT должно работать с пользовательскими типами, даже если они не могут быть созданы с помощью constexpr.   -  person StoryTeller - Unslander Monica    schedule 20.07.2017


Ответы (2)


Вы можете позволить всем этим определениям находиться в заголовочном файле, не вызывая ошибок компоновщика, когда он используется в нескольких единицах перевода, вам просто нужно также превратить их в шаблоны:

template<typename T, typename TDummy = void>
struct rsdTarget;

template<typename TDummy>
struct rsdTarget<half, TDummy>
{
    static const double value;
};

template<typename TDummy>
const double rsdTarget<half, TDummy>::value = (double)(1.0e-3);

template<typename TDummy>
struct rsdTarget<float, TDummy>
{
    static const double value;
};

template<typename TDummy>
const double rsdTarget<float, TDummy>::value = (double)(1.0e-7);

template<typename TDummy>
struct rsdTarget<double, TDummy>
{
    static const double value;
};

template<typename TDummy>
const double rsdTarget<double, TDummy>::value = (double)(1.0e-12);
person user7860670    schedule 20.07.2017

Предыдущий C++1z

template<typename>
struct rsdTarget;

template<>
struct rsdTarget<half>
{
    static constexpr double value = 1e-3;
};

// and so on...

Это имеет ограничение, заключающееся в том, что value нельзя использовать в odr, но в большинстве случаев это не проблема для констант.

После C++1z вы могли бы просто использовать переменные inline и избежать всего фиаско odr.

person Passer By    schedule 20.07.2017
comment
Вы можете избежать проблемы использования ODR, поместив определение - person M.M; 20.07.2017
comment
@MM Но тогда мы снова возвращаемся к той же проблеме только с const, что противоречит цели constexpr в данном конкретном случае. - person Passer By; 20.07.2017