在带有模板的结构中,为什么左值推导为右值?

In a struct with a template, why does an lvalue deduce as an rvalue?

当我用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

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

时,g(y) 才能调用 g(int&)

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

将模板类型指定为 int 后,T = int 并且您的函数变为 void g(const int& x)void g(int& x)。接下来,编译器将执行模板类型推导。由于您传入 long,编译器将尝试推断哪个函数适合 long 和 "best"。

由于 long 显然不是 int& 并且无法将 long 转换为 int&,编译器 "throws" 的功能消失了,只有函数 g(const int& x) 被保留。但是,long 有可能被转换为 int,这是编译器所做的,因为它是从 longint 的隐式转换,它是一个 r-值,因此调用函数 g(const int& x)

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

void g(int& x) { ... }

long y = 0;
g(y); 

这将导致错误

cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'

为什么?

一个非常量左值引用(即int&)将绑定到非-相同类型的 const lvalues.

只有const左值引用(即const int&)可以绑定到右值(这是一个临时的long 隐式转换为 int).

的对象

因此,

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

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

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

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

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

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

我认为@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;
}

以上代码片段输出以下结果:

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&),会报错candidate function not viable: no known conversion from 'long' to 'int &' for 1st argument