c++ 浮点数参数调用浮点数重载,浮点数参数调用整数重载[重复]

u59ebvdq  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(203)

此问题在此处已有答案

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));


所以它似乎故意将整数转换为浮点数,浮点数转换为整数,但我不知道为什么它会这样做,也不知道如何谷歌。为什么会发生这种情况?导致这种结果的规则是什么?

pepwfjgg

pepwfjgg1#

程序的行为可以通过引用初始化来理解。
来自dcl.init#ref-5.4:
[实施例6:

double d2 = 1.0;
double&& rrd2 = d2;                 // error: initializer is lvalue of related type
int i3 = 2;
double&& rrd3 = i3;                 // rrd3 refers to temporary with value 2.0

字符串

  • 结束示例]

案例一

在这里,我们讨论为什么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就可以绑定到物化的临时值1int类型)。

qvtsj1bj

qvtsj1bj2#

为了避免考虑隐式转换,使用C++17语法重写代码(因为您在Compiler Explorer链接中使用了该语法)。至少错误代码现在不会编译。
通过显式约束,类型必须完全匹配,并且不考虑隐式转换(C++20语法会更好一点)。
演示:https://godbolt.org/z/MexsGT55T

#include <iostream>
#include <type_traits>

template<typename type_t>
auto f(type_t&& f) -> std::enable_if_t<std::is_same_v<type_t, float>, void>
{
    std::cout << f << "f ";
}

template<typename type_t>
auto f(type_t&& f) -> std::enable_if_t<std::is_same_v<type_t, int>, void>
{
    std::cout << f << "f ";
}

int main()
{
    int iv = 2;
    float fv = 1.0f;

    f(2);  f(1.0f);
    f(iv); f(fv); // <== Will no longer compile now
}

字符串

9njqaruj

9njqaruj3#

为了避免隐式转换,请提供一个删除函数模板,如果非模板化重载不能完全匹配,则会选择该模板。也就是说,只需添加此重载:

template<typename T>
void f(T&&) = delete;

字符串

相关问题