Вывод ссылок на const из аргументов rvalue

Хорошо, это может показаться глупым вопросом, но вот оно:

template <typename T>
void foo(T& x)
{
}

int main()
{
    foo(42);
    // error in passing argument 1 of 'void foo(T&) [with T = int]'
}

Что мешает С++ создавать шаблон функции foo с T = const int вместо?

Ответ 1

Проблема заключается в том, что вывод типа шаблона должен выработать точное соответствие, и в этом конкретном случае из-за ссылки в сигнатуре точное соответствие требует lvalue. Значение 42 не является значением lvalue, а скорее rvalue, а разрешение T с const int не даст идеального соответствия. Поскольку вывод типа шаблона ограничен точными совпадениями, этот вывод не допускается.

Если вместо использования литерала вы используете не изменяемое значение lvalue, тогда компилятор будет присвоить тип соответствующим образом, поскольку const int станет идеальным совпадением для аргумента:

const int k = 10;
foo( k );            // foo<const int>( const int & ) is a perfect match

Теперь существует специальное правило, которое позволяет вызывать функцию, которая принимает константную ссылку (nonmutable lvalue) с rvalue, что подразумевает создание временного lvalue, которое позже привязано к ссылке, но для этого правила, чтобы пинать функция должна иметь эту подпись перед рукой, поэтому явным образом заявляю, что тип шаблона const int работает: foo<const int>(42).

Ответ 2

Их правила;-). Если вы оставите компилятор для вывода типа из аргумента, он выбирает самое простое, что может.

Мне это не кажется необоснованным. Ваш шаблон говорит, что ожидает неконстантную ссылку, поэтому он не компилируется с rvalue.

Вы можете либо сказать, что вы имеете в виду на сайте вызова: foo<int const>(42);, либо изменить свой шаблон, чтобы он дал понять, что ему не нужна изменчивая ссылка: template <typename T> void foo(T const & x) { }.

В С++ 11 у вас есть больше возможностей для выражения того, что ваш шаблон будет и не примет.

Ответ 3

Будь это шаблон или нормальные функции, rvalue не может быть передано по ссылке. (поэтому const T& работает, но не T&).

What is preventing C++ to instantiate the foo function template with T = const int instead?

Предположим, что С++ разрешает и делает T = const int. Теперь после некоторого времени вы меняете foo как

template<typename T>
void foo (T& x)
{
  x = 0;
}

Теперь компилятор должен генерировать ошибку. Для конечного пользователя опыт будет странным, так как для корректного оператора типа x = 0; он начал давать ошибку. Это может быть причиной того, что компилятор предотвращает на первом этапе!