【问题标题】:Template accepts const but not literal模板接受 const 但不接受字面量
【发布时间】:2019-02-04 04:42:54
【问题描述】:

在编写模板时,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 一致)

【问题讨论】:

标签: c++ templates generic-programming


【解决方案1】:

int 文字的类型为int,而不是const int。因此T 被推导出为intint&amp; can't bind to a prvalue

编写这样一个函数的正确方法是要么完善转发参数,要么使用const T&amp;,两者都可以绑定到任何东西。

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);编译需要两个模板参数,因为它们推导的类型不同。

【讨论】:

  • const T&amp; 具有允许int a; const int b; min(a, b) 的额外好处:毫无疑问T 应该推断为int 还是const int
  • @hvd 好点,完美转发的版本有简单的修复方法吗?还是很乏味?还是实际上没什么大不了的?
  • max/min 的正确版本应该有 2 个模板参数 SFINAE 排除不可比较的版本。但我怀疑 OP 需要所有的复杂性。我相信他关于文字而不是最大功能的问题
  • @PasserBy 我认为你需要有两个模板参数并返回 std::common_type_t&lt;T1&amp;&amp;, T2&amp;&amp;&gt; 才能让它工作。 ixSci 提到的 SFINAE 应该已经隐式处理了。
  • @hvd 我想知道std::common_type 是否正确。理想情况下,函数应该返回一个具有两个相同类型的 xvalue 作为参数的 xvalue,std::common_type 不支持。但是通过所有案例,我认为这只是方式太乏味了。
【解决方案2】:

int 字面量不是 const int 吗?

不,它只是一个int,而不是const,和is defined as a prvalue,因此左值引用不能绑定到它——就像你的情况一样。

使用这样的原始模板可以轻松纠正:

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

const T&amp; 也将绑定到 rvalues。

避免更改或添加任何类似的内容:

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

因为这里我们不会从具体化的临时创建副本,因此我们有返回悬空引用的风险。请参阅[class.temporary]

在函数调用中绑定到引用参数的临时对象 ([expr.call]) 一直持续到完整表达式完成 包含调用。

...在这一点上它死了。因此有悬垂性。

【讨论】:

  • 哪个版本的标准开始允许T&amp;&amp;语法?
  • @IvanRubinson 从 C++11 开始。添加了一个编辑以使其更清晰。
  • 你会遇到min(T&amp;&amp; a, T&amp;&amp; b)的问题,ab应该被推断为相同的类型。 min(T&amp;&amp; a, U&amp;&amp; b) 将是有效的。
  • @Jarod42 对不起,我匆忙打错了更正。道歉。
  • 这将在使用临时对象时返回一个悬空引用。见this answer
【解决方案3】:

Literal 产生一个T&amp; 不能接受的prvalue 表达式。 T&amp; 只接受左值。

你可以这样想:整数字面量是一个“无生命”的东西,因为它在任何地方都没有地址,你怎么能把它绑定到左值引用然后修改它呢?该对象将位于何处?更改会写在哪里?

【讨论】:

  • 但是 const 可能被优化为“非生命”
  • @IvanRubinson,嗯,不完全是。它可能被放入只读部分。所以它会有一个地址,但你不能修改它的值。另一方面,文字在没有“物化”(分配给某个变量)时不存在
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
相关资源
最近更新 更多