整数参数调用 float 重载和 float 参数调用整数重载?[复制]

Integer parameter calls float overload and float parameter calls integer overload? [duplicate]

提问人:user22879756 提问时间:11/8/2023 最后编辑:user12002570user22879756 更新时间:11/9/2023 访问量:195

问:

今天我遇到了大致以下代码:

#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 链接

按预期打印前两个 f 调用。2i 1f

现在对于第二行,我本来希望它要么根本不编译,因为 iv 和 fv 不是临时的(因此不能绑定到 r 值引用),要么它创建变量的副本以传递给函数,从而第二次打印。2i 1f

然而,不知何故,它打印了 ,这几乎是我所期望的最后一件事。2f 1i

如果将代码复制到 cppinsights 中,它会将调用转换为

f(static_cast<float>(iv));
f(static_cast<int>(fv));

所以它似乎非常有意地将整数转换为浮点数,将浮点数转换为整数,但我不知道它为什么要这样做,也不知道如何谷歌搜索。为什么会这样?导致此结果的规则是什么?

C++ Reference 重载 lvalue-to-rvalue

评论

1赞 Eljay 11/8/2023
f(iv);不能绑定到 ,但它可以通过临时绑定。反之亦然。f(int&&)f(float&&)f(fv)
4赞 Pepijn Kramer 11/8/2023
@Eljay 有时候我只是非常讨厌隐性转换
0赞 Eljay 11/8/2023
@PepijnKramer • 肖恩·巴克斯特 (Sean Baxter) 的 New Circle 实验允许您选择退出隐式转换。
1赞 Pepijn Kramer 11/8/2023
是或 cpp2 (Herb Sutter)。就像Bjarne说的,C++中隐藏着一种更好的语言,我们只需要把它弄出来。但感谢您的参考;)
0赞 user22879756 11/8/2023
@Eljay如果它可以通过临时浮点数隐式转换为浮点数以绑定到 f(float&&),为什么它不能隐式转换为 int 以通过 int 临时绑定到 f(int&&)?充其量,我预计会出现模棱两可的重载错误或类似的东西

答:

4赞 Pepijn Kramer 11/8/2023 #1

为避免将隐式转换视为隐式转换,请像这样重写代码。使用 C++17 语法(因为你在 godbolt 链接中使用了它) 至少错误的代码现在不会编译。

通过显式约束,类型必须完全匹配,并且不考虑隐式转换。(C++ 语法会更好一些)

演示 : 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
}

评论

0赞 alagner 11/8/2023
稍微不那么丑陋(但仍然丑陋;P):godbolt.org/z/Y33WP8ae5
1赞 Pepijn Kramer 11/8/2023
或 C++20 : godbolt.org/z/z66EM7fM8
0赞 alagner 11/8/2023
您可以在 C++20 中使用 而不是,例如 :)same_asis_sameauto f(std::same_as<int> auto && f)
0赞 Pepijn Kramer 11/8/2023
@alagner 谢谢,我仍在快速学习 C++20/concepts。对它们的熟悉程度不如对SFINAE;)
7赞 user12002570 11/8/2023 #2

程序的行为可以从引用初始化中理解。

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

-end example]


案例 1

在这里,我们讨论为什么电话不可行。void f(int&& i)f(iv)

左值不能绑定到调用中的右值引用参数,因此重载不可。基本上,不允许,因为是相关类型的左值。ivivoid f(int&& i)f(iv)f(int&&)int&& i = iv;iv


案例 2

在这里,我们讨论为什么电话是可行的。void f(float&& i)f(iv)

对于调用,重载是可行的,因为在这里,首先将初始值设定项表达式隐式转换为目标 type() 的 prvalue,然后可以发生临时具体,以便参数可以绑定到该具体化的临时(即 xvalue)。f(iv)void f(float&& f)ivfloatf2.0f


同样,对于调用,重载是不可行的,因为是相关类型的左值。对于调用,可以使用重载,因为首先将初始值设定项隐式转换为 prvalue,然后发生临时具体化,以便可以绑定到具体化的临时(类型)。f(fv)void f(float&& i)fvf(fv)void f(int&& i)i1int

评论

0赞 user22879756 11/8/2023
因此,如果它是同一类型的变量,它们会使函数无法保护您(因为您显然不想将变量放入用于临时函数的函数中),但是当它不是同一类型时,那么突然它就可以了,因为无论如何您都必须创建一个临时(强制转换的结果)来调用该函数。一方面,我能看出其中的逻辑,但与此同时,这真的让我希望我学会了java。
0赞 user12002570 11/8/2023
@user22879756 基本上,您的程序与 相同。因此,这与函数无关。int iv = 2; int&& i = iv; /*won't work*/ float&& f = iv; //this will work
2赞 j6t 11/8/2023 #3

若要避免隐式转换,请提供一个删除函数模板,如果非模板化重载不完全适合,则选择该模板。也就是说,只需添加以下重载:

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