Приоритет специализации шаблона C ++. Шаблонная специализация - PullRequest
4 голосов
/ 06 апреля 2020

Тема выглядит немного запутанно, однако я не знаю, как ее сформулировать более правильно, извините =)

Давайте посмотрим следующий код

#include <iostream>

template<typename T>
void f(T value) {
  std::cout << "f<T>" << std::endl;
}

template<>
void f(int value) {
  std::cout << "f<int>" << std::endl;
}

template<typename T>
struct S {
  using type = T;
};

template<typename T>
void f(typename S<T>::type value) {
  std::cout << "f<S<T>>" << std::endl;
};

int main() {
  f(123);
  f<int>(123);
}

Вывод

$ ./testgcc 
f<int>
f<S<T>>

Итак, вопрос в том, почему первый вызов приводит к специализации f<int>, а второй с явным аргументом шаблона int приводит к вызову "templated" f<S<int>>()? Есть ли в стандарте правило, в котором говорится, как создавать шаблоны в таких ситуациях?

Заранее спасибо!

PS Протестировано с различными версиями g cc и clang. - поведение такое же. У меня нет windows системы для тестирования с MSV C, однако я тестировал на Godbolt и MSV C приводит к следующему коду:

_main   PROC
        ; ....
        push    123                           ; 0000007bH
        call    void f<int>(int)                      ; f<int>
        add     esp, 4
        push    123                           ; 0000007bH
        call    void f<int>(int)                      ; f<int>
        ; ...

Так что MSV C звонит f<int> в обоих случаях. Задокументировано ли это поведение как реализация, определенная ?

Ответы [ 2 ]

3 голосов
/ 06 апреля 2020

У вас есть UB.

В отличие от классов, функции шаблона не могут быть частично специализированными. Шаблонные функции могут быть полностью специализированными, как вы написали

template<>
void f(int value) {
  std::cout << "f<int>" << std::endl;
}

Но «частичная специализация» имеет тенденцию приводить к UB, поскольку она обрабатывается как несвязанные объявления

template<typename T>
void f(T value) {
  std::cout << "f<T>" << std::endl;
}

template<typename T>
void f(typename S<T>::type value) {
  std::cout << "f<S<T>>" << std::endl;
};
// These two are conflicting declarations for "f" and compiler has no way to disambiguate.
// Worse due to nature of templates it tends to fail to figure out that there is ambiguity. 
// How to differentiate the two anyways?

Используйте SFINAE для явного объявления к каким именам относится каждое объявление f, поэтому нет конфликтов.

0 голосов
/ 06 апреля 2020

Давайте начнем с простого случая: f(123);

В template<typename T> void f(typename S<T>::type), T не может быть выведено.

Таким образом, только одна жизнеспособная функция f<T>(T) с T=int

Мы выбираем f<T>(T) (выбор происходит только с основным шаблоном), который разрешается в специализации f<int>(int).

Второй случай: f<int>(123);

Теперь обе функции жизнеспособны:

  • template<typename T> void f(typename S<T>::type) с T=int
  • f<T>(T) с T=int

Первый более специализирован, чем второй один, поэтому выбирается первый.

Примечание:
G cc, Clang и Mscv согласны Демо .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...