【问题标题】:Why is a double convertible to a const-reference of seemingly any primitive?为什么 double 可以转换为看似任何原语的 const 引用?
【发布时间】:2019-10-27 04:54:03
【问题描述】:

考虑以下代码:

#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;
}

尽管我将double 传递给一个函数,该函数接受一个对小于double 的类型的常量引用(在我的系统上,sizeof(double) == 8,而@987654327 @ 和 sizeof(char) == 1 的定义)。如果引用不是const,则编译失败(例如,float func(char &amp; val1, unsigned int &amp; val2) 而不是当前定义)并出现错误:

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

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

【问题讨论】:

标签: c++ language-lawyer reference-binding


【解决方案1】:

确实是标准的。

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

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

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

float func(double, double) = delete;

【讨论】:

  • -Wall -pedantic 还不够吗?我有那套(如问题中所述)
  • 不。有一个单独的用于缩小转化范围。
  • 我真的希望 gcc 有一个 -Wabsolutely-everything-and-if-i-complain-im-an-idiot 选项。 -Wextra 会给你-Wconversion 吗?
  • @Omnifarious:如果我能进入 gcc 开发团队(上帝保佑!),那将是我的第一个项目。
  • @MaxLanghof - 给出的推理忽略了一个重要案例:我想知道有哪些警告,以便我可以自己关闭无用的警告。由于各种原因,该手册是不够的。即使它是完整的(我怀疑它经常不是),手册中的解释也只能到此为止。真的,我想知道什么样的代码会触发警告,然后去调查并弄清楚我是否真的关心。
【解决方案2】:

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

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

float func(double, double) = delete;

也可以从另一种方式处理它:即删除所有与您的预期参数类型不完全匹配的重载。如果您想避免 任何 隐式转换(包括提升),您可以将 func 定义为已删除的 non-overloaded 函数模板,并仅为特定类型的参数定义 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;
}

再次强调,如果打算重载主函数模板,作为重载和显式专用函数模板的重载决议can be somewhat confusing,则要小心,因为特化不参与重载决议的第一步。

【讨论】:

    猜你喜欢
    • 2020-07-20
    • 2015-10-15
    • 2020-06-10
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多