Шаблон принимает const, но не буквальный - PullRequest
0 голосов
/ 29 августа 2018

При написании шаблона class T может быть заменен типом const.

Рассмотрим:

template<class T> T& min(T& a, T& b) {
    return a < b ? a : b;
}

Это будет работать в следующих случаях:

int a = 1, b = 5;
const int c = 1, d = 5;
min(a, b); // T is int
min(c, d); // T is const int

Но выдает ошибку компиляции при вызове с литералом (например):

min(1, 5); // T is const int literal

недопустимая инициализация неконстантной ссылки типа "int &" из значения типа "int"

Почему? Разве литерал int не является const int? А как можно изменить шаблон, чтобы он позволял работать с литералами?

(в соответствии с gcc 6.3 и MSVC 2015)

Ответы [ 3 ]

0 голосов
/ 29 августа 2018

int литералы имеют тип int, а не const int. Следовательно, T выводится как int, а int& не может связываться с prvalue .

Правильный способ написания такой функции - либо усовершенствовать аргументы вперед, либо использовать const T&, оба из которых могут связываться с чем угодно.

template<typename T, typename U>
auto min(T&& a, U&& b) -> decltype(a < b ? std::forward<T>(a) : std::forward<U>(b))
{
    return a < b ? std::forward<T>(a) : std::forward<U>(b); 
}

// Or...
template<typename T>
const T& min(const T& a, const T& b)
{
    return a < b ? a : b;
}

В случае идеальной пересылки аргументов два параметра шаблона необходимы для того, чтобы int a{}; min(a, 42); компилировался, поскольку их выведенные типы различны.

0 голосов
/ 29 августа 2018

Разве int не является константным int?

Нет, это просто int, а не const, а - это , определенное как prvalue , следовательно, ссылка на значение не может привязаться к нему - как в вашем случае.

Легко исправляется, если исходный шаблон выглядит так:

template<typename T>
const T& min(const T& a, const T& b){
    return a < b ? a : b;
}

как const T& будет также привязываться к rvalue s.

Избегайте изменения или добавления чего-либо подобного этому:

template<typename T, typename U>
auto&& min(T&& a, U&& b){
    return std::forward<T>(a < b ? a : b); 
}

, поскольку здесь мы не создаем копию из материализованного временного объекта, и поэтому мы рискуем вернуть висящую ссылку . Смотрите здесь в [class.tevent] :

Временный объект, связанный со ссылочным параметром в вызове функции ([expr.call]) сохраняется до завершения полного выражения содержащий вызов.

... в этот момент он умирает. Отсюда и свисание.

0 голосов
/ 29 августа 2018

Литерал создает выражение prvalue, которое T& не может принять. T& принимает только lvalues.

Вы можете думать об этом следующим образом: целочисленный литерал - это «неживая» вещь, поскольку он нигде не имеет адреса, как вы можете связать его со ссылкой на lvalue, а затем изменить его? Где этот объект будет расположен? Где будут написаны изменения?

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