这不是一篇文章,原是图表分享形式,后因代码过多,转换成了文本,故仅有少数说明文字。
1void f(int); // #1
2void f(int&); // #2
3void f(int const&); // #3
4void f(int&&); // #4
5void f(int const&&); // #5
#1
is a function that accepts borrowed int arguments.#2
is a function that takes an lvalue reference.#3
is a function that takes a const lvalue reference, which accepts both lvalue and xvalue arguments.#4
is a function that takes an rvalue reference.#5
is a function that takes a const rvalue reference, which accepts an xvalue with a const qualifier.All references bind to lvalues/xvalues.
Numeric literals are non-const.
Case 1 (#1 and #2)
1int x = 1;
2f(x); // ambiguous!
1f(+x); // calls #1 by using the unary plus
2f(int(x)); // calls #1 by explicitly copying the object
3f(const_cast<int const&>(x)); // calls #1 by using const_cast
4f(std::cref(x)); // calls #1 by using std::cref
5f(static_cast<int&&>(x)); // calls #1 by casting the argument type
6static_cast<void(*)(int)>(f)(x); // calls #1 by casting the function type
7static_cast<void(*)(int&)>(f)(x); // calls #2 by casting the function type
Case 2 (#1, #2, and #3)
1int x = 1;
2f(x); // ambiguous!
1static_cast<void(*)(int)>(f)(x); // calls #1 by casting the function type
2static_cast<void(*)(int&)>(f)(x); // calls #2 by casting the function type
Case 3 (#2 and #3)
#2
is the better choice for lvalue references, while #3
is preferable for const lvalue references. The prvalue is materialized and the const lvalue reference binds it.1int x = 1;
2int const& y = x;
3f(x); // calls #2
4f(+x); // calls #3
5f(const_cast<int const&>(x)); // calls #3
6f(1); // calls #3
7f(y); // calls #3
Case 4 (#1 and #3)
#1
and #3
for the compiler.1int x = 1;
2int const& y = x;
3f(x); // ambiguous
4f(y); // ambiguous
5f(const_cast<int const&>(x)); // ambiguous
6f(+x); // ambiguous
7f(static_cast<int&&>(x)); // ambiguous
8f(1); // ambiguous
9static_cast<void(*)(int)>(f)(x); // calls #1
10static_cast<void(*)(int const&)>(f)(x); // calls #3
Case 5 (#3 and #5)
#5
is better than the rank of #3
for rvalues.1f(x); // calls #3
2f(+x); // calls #5
3f(static_cast<int&&>(x)); // calls #5
4f(const_cast<int const&>(x)); // calls #3
5f(int(x)); // calls #5
6static_cast<void(*)(int const&)>(f)(x); // calls #3
7static_cast<void(*)(int const&&)>(f)(x); // error! cannot bind rvalue reference to lvalue
Case 6 (#3 and #4)
Case 7 (#4 and #5)
#5
is less good than #4
because binding int const&&
to xvalues requires a qualification conversion.1int x = 1;
2int const&& y = 1;
3f(x); // error! cannot bind rvalue reference to lvalue
4f(+x); // calls #4
5f(static_cast<int&&>(x)); // calls #4
6f(int(x)); // calls #4
7f(const_cast<int const&&>(y)); // calls #5
8f(const_cast<int&&>(y)); // calls #4
9f(static_cast<int const&&>(y)); // calls #5
10f(static_cast<int&&>(y)); // error! casts `const int` to `int&&`
11f(std::move(y)); // calls #5
12f(1); // calls #4
13f(static_cast<int const&&>(1)); // calls #5
Case 8 (#1 and #4)
1int x = 1;
2int&& y = 1;
3f(x); // calls #1
4f(+x); // ambiguous
5f(y); // calls #1
6f(std::move(y)); // ambiguous
7f(1); // ambiguous
8f(static_cast<int>(1)); // ambiguous
9f(static_cast<int&&>(1)); // ambiguous
10f(const_cast<int const&&>(y)); // calls #1
11f(std::move(x)); // ambiguous
12static_cast<void(*)(int&&)>(f)(std::move(y)); // calls #4
13static_cast<void(*)(int&&)>(f)(+x); // calls #4
14static_cast<void(*)(int)>(f)(+x); // calls #1
15static_cast<void(*)(int&&)>(f)(1); // calls #4
16static_cast<void(*)(int)>(f)(1); // calls #1