此问题在此处已有答案:
Unexpected output when invoking overloaded functions with different rvalue reference types(1个答案)
两个月前就关门了。
今天我遇到了大致如下的代码:
#include <iostream>
void f(float&& f) { std::cout << f << "f "; }
void f(int&& i) { std::cout << i << "i "; }
int main()
{
int iv = 2; float fv = 1.0f;
f(2); f(1.0f);
f(iv); f(fv);
}
字符串
Godbolt link的
前两个f调用打印2i 1f
,正如预期的那样。
现在,对于第二行,我希望它根本无法编译,因为iv和fv不是临时变量(因此不能绑定到r值引用),或者它创建变量的副本传递给函数,因此第二次打印2i 1f
。
然而,不知何故,它打印了2f 1i
,这是我最不希望看到的。
如果将代码复制到cppinsights中,它会将调用转换为
f(static_cast<float>(iv));
f(static_cast<int>(fv));
型
所以它似乎故意将整数转换为浮点数,浮点数转换为整数,但我不知道为什么它会这样做,也不知道如何谷歌。为什么会发生这种情况?导致这种结果的规则是什么?
3条答案
按热度按时间pepwfjgg1#
程序的行为可以通过引用初始化来理解。
来自dcl.init#ref-5.4:
[实施例6:
字符串
案例一
在这里,我们讨论为什么
void f(int&& i)
不适用于调用f(iv)
。左值
iv
不能绑定到void f(int&& i)
中的右值引用参数i
,用于调用f(iv)
,因此重载f(int&&)
不可行。基本上,不允许int&& i = iv;
,因为iv
是相关类型的左值。案例二
在这里,我们讨论为什么
void f(float&& i)
对于调用f(iv)
是可行的。对于调用
f(iv)
,重载void f(float&& f)
是可行的,因为这里首先将初始化表达式iv
隐式转换为目标类型(float
)的纯右值,然后可以发生临时物化,从而可以将参数f
绑定到物化的临时2.0f
(这是一个xvalue)。类似地,对于调用
f(fv)
,重载void f(float&& i)
是不可行的,因为fv
是相关类型的左值。对于调用f(fv)
,可以使用重载void f(int&& i)
,因为首先初始化器被隐式转换为纯右值,然后发生临时物化,这样i
就可以绑定到物化的临时值1
(int
类型)。qvtsj1bj2#
为了避免考虑隐式转换,使用C++17语法重写代码(因为您在Compiler Explorer链接中使用了该语法)。至少错误代码现在不会编译。
通过显式约束,类型必须完全匹配,并且不考虑隐式转换(C++20语法会更好一点)。
演示:https://godbolt.org/z/MexsGT55T
字符串
9njqaruj3#
为了避免隐式转换,请提供一个删除函数模板,如果非模板化重载不能完全匹配,则会选择该模板。也就是说,只需添加此重载:
字符串