Я полностью смущен после прочтения вопроса Как сделать эти параметры std:: function однозначными?, до сих пор я думал, что понял, что частично упорядочивает функциональные шаблоны, но после прочтения этого вопроса я записал три примера, чтобы проверить поведение компилятора, и полученные результаты мне трудно понять.
Пример # 1
template <class T>
void foo(T) {}
template <class T>
void foo(T&) {}
int main()
{
int i;
foo<int>(i); // error: call is ambiguous!
}
Вопрос: Обе функции жизнеспособны, что очевидно, но не та, которая принимает T&
более специализированную, чем T
? Вместо этого компилятор вызывает неоднозначную ошибку вызова.
Пример # 2
#include <iostream>
template <class T>
struct X {};
template <>
struct X<int>
{
X() {}
X(X<int&> const&) {} // X<int> is constructible from X<int&>
// note: this is not a copy constructor!
};
template <>
struct X<int&>
{
X() {}
X(X<int> const&) {} // X<int&> is constructible from X<int>
// note: this is not a copy constructor!
};
template <class T>
void bar(X<T>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <class T>
void bar(X<T&>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main()
{
bar<int>(X<int>()); // calls void bar(X<T>) [with T = int]
bar<int>(X<int&>()); // calls void bar(X<T&>) [with T = int]
}
Вопрос: Если T&
и T
неоднозначны в примере # 1, то почему здесь нет вызова неоднозначно? X<int>
является конструктивным из X<int&>
, а также X<int&>
является конструктивным из X<int>
благодаря предоставленным конструкторам. Это потому, что созданный компилятор X<int>::X(X<int> const&)
copy-constructor является лучшей последовательностью преобразования, чем X<int>::X(X<int&> const&)
(если да, то что делает лучше, обратите внимание, что аргументы передаются по значению), поэтому упорядочение специализаций вообще не имеет значения
Пример # 3
#include <iostream>
// note: a new type used in constructors!
template <class U>
struct F {};
template <class T>
struct X
{
X() {}
template <class U>
X(F<U> const&) {} // X<T> is constructible from any F<U>
// note: it takes F type, not X!
};
template <class T>
void qux(X<T>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
template <class T>
void qux(X<T&>) { std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main()
{
qux<int>(F<int>()); // calls void qux(X<T&>) [with T = int]
qux<int>(F<int&>()); // calls void qux(X<T&>) [with T = int]
}
Вопрос: Теперь это аналогичный сценарий "сопоставление лямбда [](int){}
с std::function<void(int&)>
и std::function<void(int)>
" из связанного вопроса. Почему в обоих вызовах выбирается более специализированный шаблон функции? Это потому, что последовательность преобразования одинакова, поэтому частичное упорядочивание начинает иметь значение?
Все тесты, выполненные в GCC 4.9.0 с -std=c++11
и без дополнительных флагов.