Подтвердить что ты не робот

Зависимость функции шаблона от идентичных подписей, почему это работает?

Минимальная программа:

#include <stdio.h>

#include <type_traits>

template<typename S, typename T>
int foo(typename T::type s) {
    return 1;
}

template<typename S, typename T>
int foo(S s) {
    return 2;
}

int main(int argc, char* argv[]) {
    int x = 3;
    printf("%d\n", foo<int, std::enable_if<true, int>>(x));

    return 0;
}

выход:

    1 

Почему это не дает ошибку компиляции? Когда код шаблона генерируется, не будут ли функции int foo(typename T::type search) и int foo(S& search) иметь одну и ту же подпись?

Если вы немного измените сигнатуры функций шаблона, он все равно работает (как я ожидал бы, учитывая приведенный выше пример):

template<typename S, typename T>
void foo(typename T::type s) {
    printf("a\n");
}

template<typename S, typename T>
void foo(S s) {
    printf("b\n");
}

Однако это еще не так, но единственная разница в том, что у одного есть int-подпись, а другая определяется с помощью первого параметра шаблона.

template<typename S, typename T>
void foo(typename T::type s) {
    printf("a\n");
}

template<typename S, typename T>
void foo(int s) {
    printf("b\n");
}

Ошибка компилятора (Clang):

test.cpp:26:2: error: call to 'foo' is ambiguous
foo<std::enable_if<true, int>>(3);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:16:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(typename T::type s) {
        ^
test.cpp:21:6: note: candidate function [with T = std::__1::enable_if<true, int>]
void foo(int s) {
        ^
1 error generated.

Я использую код, похожий на этот проект, над проектом, над которым я работаю, и я боюсь, что на некоторых языках, которые я не понимаю, это приведет к некоторому поведению undefined в некоторых случаях. Я должен также упомянуть, что он компилируется как для Clang, так и для VS11, поэтому я не думаю, что это просто ошибка компилятора.


Изменить: исправленный второй случай (опечатка); добавлено сообщение об ошибке от Clang.

Изменить №2: для тех из вас, кто задал вопрос, что означает тип T::.

Из http://en.cppreference.com/w/cpp/types/enable_if:

шаблон < bool B, класс T = void > struct enable_if;

Если B истинно, std:: enable_if имеет открытый тип typedef участника, равный до Т; в противном случае нет члена typedef.

enable_if - это структура. В принципе, если выражение, оцененное в первом параметре шаблона enable_if, является истинным (а в случае с моими примерами выше), тогда будет публичный член type, который имеет тот же тип, что и второй параметр шаблона.

В случае enable_if<true, int> тип enable_if:: имеет тип int.

4b9b3361

Ответ 1

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

Функция

int foo(typename T::type)

может соответствовать

template <typename S,typename T> int foo(S s)

используя T:: type в качестве значения параметра S, но

int foo(S s)

не будет соответствовать

template <typename S,typename T> int foo(typename T::type)

потому что T не может быть выведено.

Логика изложена в стандарте С++ 03 в разделе 14.5.5.2 и в стандарте С++ 11 в разделе 14.5.6.2.

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

Ответ 2

Здесь еще большее упрощение явления:

#include <stdio.h>

template<typename T>
void foo(int arg) {
    printf("a\n");
}

template<typename T>
void foo(T arg) {
    printf("b\n");
}

int main(int argc, char* argv[]) {
    foo<int>(3);   // prints "a"
    foo(3);        // prints "b"

    return 0;
}

Параметры шаблона могут быть либо пропущены через угловые скобки, либо они могут быть разрешены путем вычета. Если параметр не указан явно, он должен быть выведен с использованием аргументов функции.

Итак, в случае foo(3) шаблон "a" не будет работать, потому что параметр T явно не указан и не может быть выведен. В случае foo<int>(3) оба шаблона могут работать. На самом деле, если вы закомментируете шаблон "a" , вызов foo<int>(3) будет печатать "b". Итак, вопрос в том, почему шаблон "а" предпочтительнее? Ключевым моментом здесь является "частичный заказ":

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fpartial_ordering_funct_templ.htm

Теперь я вижу, что кто-то еще ответил (я плохо отвечаю на вопросы быстро), поэтому я собираюсь просто обернуть это сейчас и сказать, что шаблон "a" более специализирован, как сказал Вон.