5

考虑以下代码:

#include <iostream>

float func(char const & val1, unsigned int const & val2)
{
    return val1 + val2;
}

int main() {
    double test1 = 0.2;
    double test2 = 0.3;

    std::cout << func(test1, test2) << std::endl;

    return 0;
}

尽管我将 a 传递double给一个函数,该函数接受对小于 a 的类型的 const 引用double(在我的系统上,sizeof(double) == 8, whilesizeof(unsigned int) == 4sizeof(char) == 1根据定义),但它仍会编译并运行。如果引用不是const,则编译失败(例如, float func(char & val1, unsigned int & val2)而不是当前定义)并出现错误:

无法将“char&”类型的非常量左值引用绑定到“char”类型的右值

在 Godbolt 上使用 GCC、Clang、ICC 和 MSVC 进行测试时,我得到了完全相同的行为,因此它看起来是标准的。什么是 const-references 导致它被接受,而引用却不是?另外,我使用-Wall -pedantic了 - 为什么我没有收到有关缩小转换的警告?当函数通过值而不是通过引用传递时,我会这样做......

4

2 回答 2

9

这确实是标准的。

test1test2转换为匿名临时 char和函数中unsignedconst引用是适当绑定的类型。如果您将编译器设置为警告您缩小转换(例如 -Wconversion),它会输出一条消息。

如果函数参数是非引用的,则这些绑定是不可能的const,并且您的编译器在这种情况下正确地发出诊断。

一种解决方法是delete更好的重载匹配:

float func(double, double) = delete;
于 2019-06-12T15:58:44.807 回答
4

作为已接受答案的补充,尤其是方法

一种解决方法是删除更好的重载匹配:

float func(double, double) = delete;

也可以从另一种方式处理它:即删除所有与您预期的参数类型不完全匹配的重载。如果您想避免任何隐式转换(包括提升),您可以定义func为已删除的非重载函数模板,并func仅为您希望重载的特定类型的参数定义显式特化。例如:

// Do not overload the primary function template 'func'.
// http://www.gotw.ca/publications/mill17.htm
template< typename T, typename U >
float func(const T& val1, const U& val2) = delete;

template<>
float func(char const& val1, unsigned int const& val2)
{
    return val1 + val2;
}

int main() {
    double test1 = 0.2;
    double test2 = 0.3;
    char test3 = 'a';
    unsigned int test4 = 4U;
    signed int test5 = 5;

    //(void)func(test1, test2); // error: call to deleted function 'func' (... [with T = double, U = double])
    //(void)func(test2, test3); // error: call to deleted function 'func' (... [with T = double, U = char])
    (void)func(test3, test4); // OK
    //(void)func(test3, test5); // error: call to deleted function 'func' (... [with T = char, U = int])
    return 0;
}

如果打算重载主函数模板,再次强调要小心,因为重载和显式专用函数模板的重载决策可能会有些混乱,因为特化不参与重载决策的第一步。

于 2019-06-12T16:42:59.400 回答