在带有模板的结构中,为啥左值会推导出为右值?

Posted

技术标签:

【中文标题】在带有模板的结构中,为啥左值会推导出为右值?【英文标题】:In a struct with a template, why does an lvalue deduce as an rvalue?在带有模板的结构中,为什么左值会推导出为右值? 【发布时间】:2018-06-22 03:41:22 【问题描述】:

当我使用long 变量调用g() 函数时,它为什么调用g(const T& x)

如果我将long 更改为int,它会改为调用g(T& x)

#include <iostream>

template <typename T>
struct s

    void g(const T& x)  std::cout << "1\n";
    void g(T& x)  std::cout << "2\n";
;

int main() 
    s<int> x;

    long y = 0;
    x.g(y);

结果:

1

【问题讨论】:

你没有用左值调用它。 在调用站点可能存在从longint 的隐式转换,因此传递的是右值。 【参考方案1】:

非常量 int&amp; 引用只能绑定到 int 变量,不能绑定其他任何东西。 longint 是独立且不同的类型,因此不能将 long 变量分配给非常量 int&amp; 引用。只有当yint 时,g(y) 才能调用g(int&amp;)

定义了从longint 的隐式转换,并且const 引用可以绑定到临时变量。当ylong 时,g(y) 无法调用g(int&amp;),但编译器可以从long 值创建一个临时的int 变量来调用g(const int&amp;)

【讨论】:

【参考方案2】:

简化您的代码以清楚地查看错误:

void g(int& x)  ... 

long y = 0;
g(y); 

这将导致错误

无法将“int&”类型的非常量左值引用绑定到 输入'int'

为什么?

非常量左值引用(即int&amp;)将绑定到相同类型的非常量左值

只有const左值引用(即const int&amp;)可以绑定到右值(这是一个由long隐式转换为@987654326而产生的临时对象@)。

因此,

void g(const T& x)  std::cout << "1\n";

将在您给定的代码中通过重载决议进行选择。

【讨论】:

【参考方案3】:

函数调用检查重载函数的最近匹配。

它使用任何现有的非常量函数调用检查输入参数的类型 - 在本例中为 long。 g(int&) 不匹配 long,因此被丢弃。

接下来它检查第二个函数 g(const int&)。这也不匹配。

它通过向下转换输入参数来检查它是否可以匹配其中任何一个。将 y 从 long 向下转换为 int。由于这种向下转换会产生一个右值,并且只有 const 左值引用可以绑定到右值,所以调用 g(const int&)。

【讨论】:

【参考方案4】:

将模板类型指定为intT = int 后,您的函数将变为void g(const int&amp; x)void g(int&amp; x)。接下来,编译器将执行模板类型推导。由于您传入long,编译器将尝试推断哪个函数适合long“最佳”。

由于long 显然不是int&amp; 并且无法将long 转换为int&amp;,因此编译器“丢弃”了该函数,只剩下函数g(const int&amp; x)。但是,long 可以转换为 int,这是编译器所做的,因为它是从 longint 的隐式转换,它是一个 r 值,因此函数 @987654336 @ 被调用。

【讨论】:

【参考方案5】:

我认为@codekaizer 和@RemyLebeau 给出了正确的答案。您只能将右值绑定到 const 左值引用。我做了一个测试。

#include "print_type.h"
#include <iostream>

template<typename T> struct s 
  void foo(const T& t)  std::cout << "calling foo(const " << type_name<T>() << "&)\n"; 
  void foo(T& t)  std::cout << "calling foo(" << type_name<T>() << "&)\n"; 
;

template<typename T>
  void bar(const T& t)  std::cout << "calling bar(const " << type_name<T>() << "&)\n"; 

template<typename T>
void bar(T& t)  std::cout << "calling bar(" << type_name<T>() << "&)\n"; 

void g(int& i)  std::cout << "calling g(int&)\n"; 
void g(const int& i)  std::cout << "calling g(const int&)\n"; 

int main() 
  s<int> x;

  int i = 0;
  x.foo(i);
  bar(i);

  const int ci = 0;
  x.foo(ci);
  bar(ci);

  long l = 0;
  x.foo(l);
  bar(l);
  g(l);

  return 0;

上面的代码sn -p输出如下结果:

calling foo(int&)
calling bar(int&)
calling foo(const int&)
calling bar(const int&)
calling foo(const int&)
calling bar(long&)
calling g(const int&)

如果我们不定义函数void g(const int&amp;),就会报错candidate function not viable: no known conversion from 'long' to 'int &amp;' for 1st argument

【讨论】:

以上是关于在带有模板的结构中,为啥左值会推导出为右值?的主要内容,如果未能解决你的问题,请参考以下文章

error C2662 无法将左值绑定到右值 —— 变量永远是左值,即使它的类型为右值引用

C++11 move()函数:将左值强制转换为右值

C++11新特性:21—— C++11 move()函数:将左值强制转换为右值

C++ std::move()函数(将一个左值强制转化为右值引用,继而可以通过右值引用使用该值)

C++ std::move()函数(将一个左值强制转化为右值引用,继而可以通过右值引用使用该值)

C++ C++11新特性--右值引用