Почему параметры компилятора влияют на выбор реализации шаблона? - PullRequest
10 голосов
/ 03 июля 2019

В зависимости от того, компилирую ли я с -O3 или без оптимизации, компилятор не выбирает одну и ту же функцию-шаблон-экземпляр.Использование gcc (Debian 8.3.0-6) 8.3.0.

Из-за недосмотра у меня есть реализация по умолчанию в объявлении шаблона функции:

#pragma once

#include <iostream>

template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here

И их специализация:

#include "func.h"

template <>
void func<1>()
{
    std::cerr << "special 1\n";
}

template <>
void func<2>()
{
    std::cerr << "special 2\n";
}

И основная функция.

#include "func.h"

int main(void)
{
    func<1>();
    func<2>();

    return 0;
}

Компиляция и запуск g++ -Wall func.cpp main.cpp -o main && ./main дает:

special 1
special 2

Использование оптимизаций g++ -O3 -Wall func.cpp main.cpp -o main && ./main дает:

default impl
default impl

Ожидается ли это?Код вызывает неожиданное поведение, о котором я не знаю?

Спасибо @NathanOliver из комментариев, которые сделали Wandbox .Компиляция с оптимизацией или без нее показывает различный вывод.

Ответы [ 2 ]

12 голосов
/ 03 июля 2019

Ваш код неверен, диагностика не требуется.Таким образом, возможно различное поведение на разных уровнях оптимизации.

[temp.expl.spec]

6 Если шаблон,шаблон члена или член шаблона класса явно специализирован, тогда эта специализация должна быть объявлена ​​перед первым использованием этой специализации, которая вызовет неявную реализацию в каждой единице перевода, в которой такое использование происходит;Диагностика не требуется.Если программа не предоставляет определения для явной специализации, и либо специализация используется таким образом, чтобы вызвать неявное создание экземпляра, либо член является функцией виртуального члена, программа не сформирована, диагностика не требуется.Неявное создание экземпляра никогда не генерируется для явной специализации, которая объявлена, но не определена.

Шаблон функции специализирован для одного TU, но для другого не доступно объявление специализации.Вполне вероятно, что агрессивный оптимизатор выбирает неявное создание экземпляров (которое доступно внутри) вместо того, чтобы найти то, что вы создали в другом месте.Решение состоит в том, чтобы объявить, что ваша специализация существует в заголовке.

3 голосов
/ 03 июля 2019

У вас неопределенное поведение из-за проблем ODR.

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

При компиляции вашего примера компилятор создаст экземпляр вашей функции.Посмотрите на это:

template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here

int main(void)
{
    func<1>();
    func<2>();

    return 0;
}

Это то, что видит компилятор.Он не может видеть другие файлы cpp.Компилятор создаст экземпляры шаблонов и создаст дополнительное определение для ваших функций.

Тогда ваш другой файл cpp предоставит другое определение, отличное.

Решением этого является прямое объявление специализаций вВаш заголовок:

template<> void func<1>();
template<> void func<2>();

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

...