Перегруженные шаблоны функций со ссылочными параметрами - PullRequest
4 голосов
/ 27 марта 2012
template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}
int main()
{
    int x;
    f(x);   //ambiguous
}

Почему этот призыв неоднозначен? Первая специализация шаблона - f <int> (int &), а вторая - f <int&> (int &). Поскольку параметры одинаковы, шаблон функции, который более специализирован в соответствии с правилами частичного упорядочения, лучше. Затем в соответствии со стандартом 14.8.2.4/9

Если для данного типа вычет выполняется успешно в обоих направлениях (т. Е. Типы идентичны после приведенных выше преобразований), и оба P и A были ссылочными типами (до замены на тип, упомянутый выше):
- если тип из шаблона аргумента был ссылкой lvalue, а тип из шаблона параметра не был, тип аргумента считается более специализированным, чем другой; ...

Первый шаблон имеет T &, а второй - T &&, поэтому первый должен быть более специализированным. Что здесь не так?


Edit: Этот код протестирован в g ++ 4.6.1 и VC ++ 2010 Express, оба дают неоднозначную ошибку.

Ответы [ 2 ]

5 голосов
/ 27 марта 2012

Руководство

Не перегружать:

template <typename T> void f(T&) {}
template <typename T> void f(T&&) {}

Причина

Для шаблона существует специальное правило удержания шаблона:

template <typename T> void f(T&&) {}

Это правило существует для того, чтобы разрешить так называемую "идеальную пересылку". Это помогает таким вещам, как bind и make_shared, полностью передавать свои аргументы, сохраняя как cv-квалификаторы, так и «категорию значений» (lvalue / rvalue-ness).

Это специальное правило гласит, что когда f(T&&) вызывается с параметром lvalue (например, int), то T выводится как ссылка lvalue (например, int&) вместо int. И ссылка rvalue на ссылку lvalue на int сворачивается до ссылки на lvalue на int. * Т.е. 1023 *

f(x)

звонки

f<int&>(int& && x);

, что упрощает до:

f<int&>(int& x);

Редактировать

Это не более или менее специализированный, чем f<int>(int&).

Спасибо Йоханнесу Шаубу за исправление (см. Комментарии).

Решение

Вы можете делать все, что захотите, с помощью одной функции:

template <typename T> void f(T&&) {}

Если T выводит в качестве ссылки lvalue, делайте все, что вы хотели сделать при первой перегрузке, иначе делайте то, что вы хотели делать при второй перегрузке:

template <class T> void f_imp(T&, std::true_type) {std::cout << "lvalue\n";}
template <class T> void f_imp(T&&, std::false_type) {std::cout << "rvalue\n";}

template <typename T> void f(T&& x)
{
     f_imp(std::forward<T>(x), std::is_lvalue_reference<T>());
}

И используйте std::forward для полной пересылки x в функцию детализации реализации.

4 голосов
/ 27 марта 2012

Ваша интерпретация стандарта представляется правильной.

template <typename T> void f(T&) {}  // #1
template <typename T> void f(T&&) {} // #2

В # 1, T успешно выводится как int, а в # 2, T успешно выводится как int&, поэтому выполняется частичное упорядочение для выбора функции для вызова. Во время частичного упорядочения для вызова f(x) будут упорядочены типы первого (только в этом случае) аргумента ( [temp.deduct.partial] / 3 bullet 1). Во время удержания в обоих направлениях тип P будет T, а тип A будет синтезированным типом, представляющим T ( [temp.deduct.partial] / 5 ), поэтому удержание преуспевает в обоих направлениях.

Как вы заметили, [temp.deduct.partial] / 9 затем применяется и говорит, что первый аргумент # 1 является более специализированным. Таким образом, [temp.deduct.partial] / 10 выбирает # 1 в качестве наиболее специализированного шаблона, а его специализация является результатом разрешения перегрузки.

Вы не упомянули, какой компилятор вы используете. Я предполагаю, что это g ++ - похоже, это ошибка в этом компиляторе (я тестировал версии между 4.4.3 и 4.7, и все они отвергают этот код). clang принимает ваш код и вызывает перегрузку f(T &), как вы и ожидали.

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