Использование шаблона перед его специализацией?

Я узнал, что вы можете специализировать шаблон после, который он сначала использует, если вы используете его с использованием шаблона обертки. Простой пример:

#include <iostream>

template<typename T>
const char* templateImpl();

template<typename T>
const char* templateGetter() { return templateImpl<T>(); }

struct S{};

int main(){ std::cout << templateGetter<S>() << std::endl; return 0; }

template<>
const char* templateImpl<S>(){ return "S"; }

Это работает с каждым компилятором - я не удивлен, что MSVC компилирует его, поскольку он обрабатывает шаблоны по-разному, но GCC и clang позволяют это делать. Я думал, что стандарт требует, чтобы специализация происходила до первого использования, что в этом случае означало бы перед основной и ожидало, что они сообщают об ошибке.

Я что-то пропустил, соответствует ли этот код стандарту?

Чтобы уточнить, если я в основном меняю templateGetter<S> на templateImpl<S>, программа не будет компилироваться с сообщением об ошибке, которое я ожидал бы от этого тоже:

main.cpp: 14: 29: ошибка: специализация 'const char * templateImpl() [с T = S] 'после создания экземпляра

Ответы

Ответ 1

У вас (un) повезло. Это плохо сформированный отчет о недоставке.

[temp.expl.spec]/6-7:

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

7 Размещение явных деклараций специализации для шаблонов функций, шаблонов классов, шаблонов переменных, функций-членов шаблонов классов, статических элементов данных шаблонов классов, классов-членов шаблонов классов, членских классов класса шаблоны, шаблоны классов членов шаблонов классов, шаблоны функций-членов шаблонов классов, шаблоны элементов статических данных шаблонов классов, функции-члены шаблонов-членов шаблонов классов, функции-члены шаблонов-членов непартийных классов, шаблоны элементов статических данных, -template-классы, шаблоны-члены-члены классов-членов шаблонов классов и т.д., а также размещение деклараций частичной специализации шаблонов классов, шаблонов переменных, шаблонов классов-членов классов, отличных от шаблонов, шаблонов элементов статических данных для классов без шаблонов, шаблоны классов участников шаблонов классов и т.д. могут повлиять на то, хорошо ли сформирована программа в соответствии с относительным расположением явные декларации специализации и их точки инстанцирования в блоке перевода, как указано выше и ниже. При написании специализации будьте осторожны с ее местоположением; или сделать его компиляцией, станет таким испытанием, чтобы разжечь его самосожжение.

p7 здесь не очень полезен, но я не могу устоять перед его цитированием:)

Выполнение templateGetter<S> вызывает неявное создание объявления templateImpl<S>. Вы не видели ошибки в коде, потому что многие реализации предпочитают откладывать экземпляры шаблонов до конца единицы перевода, когда это возможно, что является допустимой техникой внедрения. (Я не буду приводить здесь стандартную версию, но вы обнаружите, что специализация шаблонов функций имеет дополнительную точку инстанцирования в конце единицы перевода.)

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

template<typename T>
auto templateGetter() { return templateImpl<T>(); }

и voila:

+ g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:29: error: specialization of 'const char* templateImpl() [with T = S]' after instantiation
 const char* templateImpl<S>(){ return "S"; }
                             ^
+ clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:13: error: explicit specialization of 'templateImpl<S>' after instantiation
const char* templateImpl<S>(){ return "S"; }
            ^
main.cpp:7:32: note: implicit instantiation first required here
auto templateGetter() { return templateImpl<T>(); }
                               ^
1 error generated.

Ответ 2

Я думаю, что это законно. Цитирование N4140, [temp.point]:

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

Затем в [temp.fct.spec]:

Функция, созданная из шаблона функции, называется функцией типовая специализация; так и явная специализация функциональный шаблон....

Иными словами, ошибка не будет возникать до тех пор, пока templateGetter() и последующая специализация templateImpl не будут созданы, как вы ожидали бы, что шаблоны будут работать.