【问题标题】:Is it possible for the template parameter to be a reference type?模板参数是否可以是引用类型?
【发布时间】:2019-04-18 12:36:07
【问题描述】:

我已经开始学习 C++,目前我正在尝试开始使用模板,如果我的措辞不是 100% 准确,请多多包涵。

我正在使用以下文献:

  • C++ 模板:完整指南(第 2 版)
  • 有效的现代 C++:改进 C++11 和 C++14 使用的 42 种特定方法

第一本书看下面的模板函数

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

并指出此定义有一个缺点,因为 T1T2 可能是引用,因此返回类型可能是引用类型。

然而,第二本书指出,如果 ParamType,在我们的例子中 T1T2 既不是指针也不是引用,这对于我们的例子是正确的,调用表达式的引用部分被忽略。

举例说明

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

现在我想知道,第一个代码 sn-p 的返回类型怎么可能是引用类型?

【问题讨论】:

  • max&lt;const int&amp;, const int&amp;&gt;(42, 51).
  • @polygamma 它不会被如此推断,但有人可以像 Jarod 在他们的评论中那样指定它。
  • 表达式在 C 或 C++ 中从不具有引用类型(是的,在 C 中没有引用,但类型系统的工作方式相同)

标签: c++ templates reference decltype template-argument-deduction


【解决方案1】:

他们都是对的:

查看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 -&gt; decltype(b&lt;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) 如果 E2 和 E3 是相同类型和相同值类别的glvalues,则结果具有相同类型和值类别[...]

5) 否则,结果为纯右值 [...]

在 C++ 中这意味着:

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 是一个左值。
  • @eerorika 'some b 和 c 的三元条件表达式'。没错,我看错了
  • @PasserBy 你好,我是否应该删除“返回...”,即使删除它会使 cmets 毫无目的?
  • @Martinm 是的,cmets 是为了改进帖子。完成后,评论可以由其所有者删除。人们被鼓励这样做。 (我会在 10 分钟后删除这条评论)
  • @MartinMorterol 对于 decltype 情况,如果函数返回引用有什么问题?你能解释一下,从书中无法理解为什么这是一个大问题。
【解决方案2】:

因为 T1 或 T2 可能是一个引用,所以返回类型可能是一个引用类型。

我认为这被错误地引用了。返回类型可能是引用类型,但原因不同。

如果您在second book 中走得更远一点。在第 3 项中,您将找到问题的答案。

将 decltype 应用于名称会产生该名称的声明类型。名称通常是 lvalue 表达式,但这不会影响 decltype 的行为。 但是,对于比名称更复杂的左值表达式,decltype 通常会确保报告的类型是左值引用。也就是说,如果名称以外的左值表达式的类型为 T,则 decltype 将该类型报告为 T&

但是,这种行为的含义值得关注。在

int x = 0;

x 是变量的名称,所以 decltype(x) 是 int。但是将名称 x 括在括号中——“(x)”——会产生一个比名称更复杂的表达式。作为一个名称,x 是一个左值,C++ 也将表达式 (x) 定义为一个左值。 decltype((x)) 因此是 int&。在名称周围加上括号可以更改 decltype 为其报告的类型!

现在剩下的唯一问题是:

b&lt;a?a:b 是左值吗?

来自cppreference

4) 如果 E2 和 E3 是相同类型和相同值的泛左值 类别,则结果具有相同的类型和值类别,并且是 如果 E2 和 E3 中的至少一个是位域,则为位域。

所以ab 是左值,如果它们属于同一类型,b&lt;a?a:b 也将是左值。看一个很好的解释here

这意味着max(1, 2) 调用的返回类型将是int&amp;,而对于max(1, 2.0) 将是int

【讨论】:

  • IMO 事实 decltype 做了两件不同的事情(只是细微的不同,并不总是不同),这在语言设计中是令人厌恶的。 OTOH static 曾经被认为是一个糟糕的设计选择,但它实际上做了两件非常不同的事情(顶层与功能块),然后是两件密切相关的事情(类静态变量与函数静态变量)
  • 非常感谢您的回答。我只想解决一件事。您写道:“我认为这是错误地引用”,书中的确切引用是:“返回类型可能是引用类型,因为在某些情况下 T 可能是引用”
猜你喜欢
  • 1970-01-01
  • 2012-10-25
  • 2019-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-19
相关资源
最近更新 更多