Возможно ли, чтобы параметр шаблона был ссылочным типом?

Я начал изучать C++, и в настоящее время я пытаюсь начать работу с шаблонами, поэтому, пожалуйста, потерпите меня, если моя формулировка не на 100% точна.

Я использую следующую литературу:

  • Шаблоны C++: полное руководство (2-е издание)
  • Эффективный современный C++: 42 конкретных способа улучшить использование C++11 и C++14

В первой книге рассматривается следующая функция шаблона

template<typename T1, typename T2>
auto max(T1 a, T2 b) -> decltype(b<a?a:b) {
  return b < a ? a : b;
}

и заявляет, что у этого определения есть недостаток, поскольку T1 или T2 могут быть ссылкой, так что возвращаемый тип может быть ссылочным типом.

Однако во второй книге говорится, что если ParamType, в нашем случае T1 и T2 не является ни указателем, ни ссылкой, что верно для нашего случая, то ссылочная часть вызывающего выражения игнорируется.

Проиллюстрировано на примере

template<typename T>
void f(T param);

int x = 27; // as before
const int cx = x; // as before
const int& rx = x; // as before
f(x); // T's and param's types are both int
f(cx); // T's and param's types are again both int
f(rx); // T's and param's types are still both int

Теперь мне интересно, как вообще возможно, что возвращаемый тип первого фрагмента кода является ссылочным типом?

max<const int&, const int&>(42, 51).
Jarod42 18.04.2019 14:39

@polygamma Это не будет выводиться как таковое, но кто-то может указать это, как это сделал Джарод в своем комментарии.

NathanOliver 18.04.2019 14:41

Выражение никогда не имеет ссылочного типа в C или C++ (да, в C нет ссылок, но система типов работает так же)

curiousguy 18.04.2019 16:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
3
833
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Они оба правы:

См. код, сгенерированный в cppinsights.

template<typename T1, typename T2>
auto max(T1 a, T2 b) -> decltype(b<a?a:b) {
  return b < a ? a : b;
}

template<typename T1, typename T2>
auto max2(T1 a, T2 b){
  return b < a ? a : b;
}

max(j,i);
max2(j,i);

Будет «генерировать»:

template<>
int & max<int, int>(int a, int b)
{
  return b < a ? a : b;
}

template<>
int max2<int, int>(int a, int b)
{
  return b < a ? a : b;
}

Проблема в C++11 -> decltype(b<a?a:b) если его удалить (в C++14 и более), функция больше не будет возвращать ссылку

static_assert( is_same_v<decltype(i),int> );
static_assert( is_same_v<decltype((i)),int&> );
static_assert( is_same_v<decltype(i+j),int> );
static_assert( is_same_v<decltype(true?i:j),int&> );

См. https://en.cppreference.com/w/cpp/language/operator_other#Conditional_operator

4) If E2 and E3 are glvalues of the same type and the same value category, then the result has the same type and value category [...]

5) Otherwise, the result is a prvalue [...]

В С++ это означает:

static_assert( is_same_v<decltype(true?i:j),int&> ); // E2 and E3 are glvalues 
static_assert( is_same_v<decltype(true?i:1),int> ); // Otherwise, the result is a prvalue

Результат ?: не всегда равен xvalue. Категория зависит от типов и категорий значений операндов. Я думаю, что a ? a : b - это lvalue.

eerorika 18.04.2019 15:20

@eerorika 'троичное условное выражение для немного b и c'. Вы правы, я неправильно прочитал

Martin Morterol 18.04.2019 15:24

@PasserBy Здравствуйте, должен ли я стереть «возврат ...», даже если стереть, он будет делать комментарии без цели?

Martin Morterol 18.04.2019 16:00

@Martinm Да, комментарии нужны для улучшения поста. После этого комментарий может быть удален его владельцем. И людей к этому призывают. (Я удалю этот комментарий через 10 минут)

Sjoerd 18.04.2019 16:03

@MartinMorterol для случая decltype, в чем проблема, если функция возвращает ссылку? Можете ли вы объяснить, из книги не могу понять, почему это большая проблема.

Daemon 29.01.2020 17:44

@Daemon Если вы запустите ссылку на cppinsights, вы получите это предупреждение warning: reference to stack memory associated with parameter 'a' returned [-Wreturn-stack-address], которое довольно явно;). При необходимости вы можете посмотреть здесь: stackoverflow.com/questions/6441218/…

Martin Morterol 30.01.2020 08:40

since T1 or T2 might be a reference, so that the return type could be a reference type.

Я предполагаю, что это неправильно процитировано. Тип возвращаемого значения может быть ссылочным, но по разным причинам.

Если вы пойдете немного дальше в своем вторая книга. В Пункт 3 вы найдете ответ на свой вопрос.

Applying decltype to a name yields the declared type for that name. Names are typically lvalue expressions, but that doesn’t affect decltype’s behavior. For lvalue expressions more complicated than names, however, decltype generally ensures that the type reported is an lvalue reference. That is, if an lvalue expression other than a name has type T, decltype reports that type as T&.

There is an implication of this behavior that is worth being aware of, however. In

int x = 0;

x is the name of a variable, so decltype(x) is int. But wrapping the name x in parentheses—“(x)”—yields an expression more complicated than a name. Being a name, x is an lvalue, and C++ defines the expression (x) to be an lvalue, too. decltype((x)) is therefore int&. Putting parentheses around a name can change the type that decltype reports for it!

Теперь остался единственный вопрос:

Является ли b<a?a:b lvalue?

От cppreference

4) If E2 and E3 are glvalues of the same type and the same value category, then the result has the same type and value category, and is a bit-field if at least one of E2 and E3 is a bit-field.

Итак, a и b являются lvalue, и если они одного типа, b<a?a:b также будет lvalue. Смотрите хорошее объяснение здесь.

Это означает, что возвращаемый тип для вызова max(1, 2) будет int&, а для max(1, 2.0) будет int

ИМО тот факт, что decltype делает две разные вещи (которые лишь немного отличаются и не всегда разные), является мерзостью в языковом дизайне. OTOH static раньше считался ужасным выбором дизайна, но на самом деле он делает две совершенно разные вещи (верхний уровень и функциональный блок), а затем две тесно связанные вещи (статическую переменную класса и статическую переменную функции).

curiousguy 18.04.2019 16:33

Большое спасибо за ваш ответ. Я просто хочу затронуть одну вещь. Вы написали: "Я предполагаю, что это неправильно процитировано", точная цитата из книги: "Может случиться так, что возвращаемый тип является ссылочным типом, потому что при некоторых условиях T может быть ссылкой"

polygamma 18.04.2019 19:58

Другие вопросы по теме