вывод ссылок на 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?


person fredoverflow    schedule 20.05.2011    source источник


Ответы (3)


Проблема в том, что для вывода типа шаблона требуется точное совпадение, а в этом конкретном случае из-за ссылки в подписи для точного совпадения требуется 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

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

person David Rodríguez - dribeas    schedule 20.05.2011
comment
+1 за указание на то, что это только вывод типа шаблона, который терпит неудачу, ничего общего с правилами о привязке ссылок, поскольку foo<const int>(42) действительно компилируется. Но более поздняя часть о запрете двух определяемых пользователем преобразований не так актуальна. - person aschepler; 20.05.2011
comment
@aschepler: вторая часть абсолютно не имеет значения, но она довольно наглядна и имеет сходство в том, что необходим дополнительный шаг, который компилятор не может предпринять. В случае вывода типа шаблона, потому что он требует полного совпадения, в случае преобразования типов, потому что в цепочке разрешено только одно пользовательское преобразование. Может быть, это не помогает прояснить суть вопроса... поэтому я удалил его. - person David Rodríguez - dribeas; 20.05.2011
comment
Я не уверен, что этот ответ правильный. Наблюдение: существует разница между аргументами класса и не класса. typedef const int X; foo( X() ); не работает, а struct Y{}; typedef const Y X; foo( Y() ); в порядке. Попытка объяснения: const значений prvalue, не относящихся к классу, не являющихся массивами, удалены как тип выражения [expr]/6; кроме того, в примере ОП 42 не является const int. - person dyp; 28.08.2014

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

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

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

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

person Alan Stokes    schedule 20.05.2011

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

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

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

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

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

person iammilind    schedule 20.05.2011
comment
Но вопрос в том, почему экземпляр шаблона создается с помощью T=int, когда T=const int будет компилироваться. - person Naveen; 20.05.2011
comment
В ответе есть доля правды, в частности точный ответ - выделенный жирным шрифтом текст: rvalue нельзя передавать по ссылке (даже const&), но тогда вы совсем немного отвлеклись... T отлично может быть выведено как const int, если фактический аргумент в вызове является неизменяемым lvalue (т. е. const int объект, а не rvalue 42), а шаблонная функция всегда проверяется после создания экземпляра, что означает, что if T выводится как const int предоставленный вами foo завершится ошибкой, как и ожидалось -- помните, что шаблон не компилируется пока не будет создан его экземпляр. - person David Rodríguez - dribeas; 20.05.2011