c++language-lawyerreference-binding

Why is a double convertible to a const-reference of seemingly any primitive?


Consider the following code:

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

This compiles and runs despite the fact that I am passing in a double to a function that takes a const-reference to types that are smaller than a double (on my system, sizeof(double) == 8, while sizeof(unsigned int) == 4, and sizeof(char) == 1 by definition). If the reference isn't const, compilation fails (e.g., float func(char & val1, unsigned int & val2) instead of the current definition) with an error:

cannot bind non-const lvalue reference of type 'char&' to an rvalue of type 'char'

I get the exact same behavior when testing this with GCC, Clang, ICC, and MSVC on Godbolt, so it appears standard. What is it about const-references that causes this to be accepted, whereas a reference isn't? Also, I used -Wall -pedantic - why am I not getting a warning about a narrowing conversion? I do when the function passes by value instead of by reference...


Solution

  • It is indeed standard.

    test1 and test2 are converted to anonymous temporary char and unsigned types for which the const references in the function are appropriate bindings. If you set your compiler to warn you of narrowing conversions (e.g. -Wconversion), it would output a message.

    These bindings are not possible if the function parameters are non-const references, and your compiler is correctly issuing a diagnostic in that case.

    One fix is to delete a better overload match:

    float func(double, double) = delete;