yohhoyの日記

技術的メモをしていきたい日記

関数型への参照型にまつわる特例ルール

プログラミング言語C++の関数型(function type)には左辺値(lvalue)しか存在しないが(→id:yohhoy:20200530)、左辺値参照型(R (&)(Args...))/右辺値参照型(R (&&)(Args...))いずれにも束縛できる。そう、よかったね。

#include <utility>
using std::move;  // 右辺値参照型(T&&)を返す関数

void f() {}
// 関数型void()
void ( &lref1)() = f;        // OK
void (&&rref1)() = f;        // OK: 右辺値参照型だがlvalue束縛可能
void ( &lref2)() = move(f);  // OK: 関数型の式move(f)はlvalue
void (&&rref2)() = move(f);  // OK: 右辺値参照型だがlvalue束縛可能
// (関数型のprvalueは存在しない)

int n = 42;
// 通常の型(int)
int&  lref3 = i;        // OK
int&& rref3 = i;        // NG: 右辺値参照型はlvalue束縛不可
int&  lref4 = move(i);  // NG: 左辺値参照型はxvalue束縛不可
int&& rref4 = move(i);  // OK
int&  lref5 = 42;       // NG: 左辺値参照型はprvalue束縛不可
int&& rref5 = 42;       // OK

関数オーバーロード解決では関数型への左辺値参照型が優先されるため、あいまいさは存在しない。(C++17 16.3.3.2/p3)

C++17 11.6.3/p5より一部引用(下線部は強調)。

A reference to type "cv1 T1" is initialized by an expression of type "cv2 T2" as follows:

  • If the reference is an lvalue reference and the initializer expression
    • is an lvalue (but is not a bit-field), and "cv1 T1" is reference-compatible with "cv2 T2", or
    • (snip)
  • Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference. (snip)
    • If the initializer expression
      • is an rvalue (but not a bit-field) or function lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or
      • (snip)