为什么选择这种转换运算符的重载?

Why is this overload of a conversion operator chosen?

考虑 following code

struct any
{
    template <typename T>
    operator T &&() const;

    template <typename T>
    operator T &() const;
};
int main()
{
    int a = any{};
}

此处第二个转换运算符由重载决策选择。为什么?

据我理解,这两个运算符分别推导为operator int &&() constoperator int &() const。两者都在可行的功能集中。通读 [over.match.best] 并没有帮助我弄清楚为什么后者更好。

为什么后者的功能比前者好?

推导的return值转换运算符有点奇怪。但核心思想是它像一个函数参数一样选择使用哪个。

并且在 T&&T& 之间做出决定时,T& 在重载决策规则中获胜。这是为了允许:

template<class T>
void f( T&& ) { std::cout << "rvalue"; }
template<class T>
void f( T& ) { std::cout << "lvalue"; }

上班。 T&& 可以匹配左值,但是当左值和通用引用重载都可用时,首选左值。

正确的转换运算符集可能是:

template <typename T>
operator T&&() &&;

template <typename T>
operator T &() const; // maybe &

甚至

template <typename T>
operator T() &&;

template <typename T>
operator T &() const; // maybe &

防止生命周期延长失败对你造成伤害。

3 The types used to determine the ordering depend on the context in which the partial ordering is done:

[截图]

(3.2) In the context of a call to a conversion function, the return types of the conversion function templates are used.

然后在选择重载时根据 "more specialized" 规则结束:

(9.1) if the type from the argument template was an lvalue reference and the type from the parameter template was not, the parameter type is not considered to be at least as specialized as the argument type; otherwise,

因此 operator T&& 至少不如 operator T& 专业,同时没有规则规定 operator T& 至少不像 operator T&& 专业,所以 operator T&operator T&&.

更专业

在其他条件相同的情况下,更专业的模板胜过更少的重载解决方案。

我们正在尝试从 any 初始化 int。它的过程:

  1. 找出我们可以做到的所有方法。也就是确定我们所有的候选人。这些来自非显式转换函数,可以通过标准转换序列 ([over.match.conv]) 转换为 int。该部分包含此短语:

    A call to a conversion function returning “reference to X” is a glvalue of type X, and such a conversion function is therefore considered to yield X for this process of selecting candidate functions.

  2. 选出最佳人选。

在第 1 步之后,我们有两个候选人。 operator int&() constoperator int&&() const,出于选择候选函数的目的,两者都被认为产生 int最佳 候选人int

我们确实有一个决胜局,它更喜欢左值引用而不是右值引用 ([over.ics.rank]/3.2.3)。不过,我们在这里并没有真正绑定一个引用,而且那里的例子有些颠倒——这是针对参数是左值与右值引用的情况。

如果这不适用,那么我们将进入 [over.match.best]/2.5 首选更专业的函数模板的决胜局。

一般来说,经验法则是更具体的转换是最佳匹配。左值引用转换函数比转发引用转换函数更具体,因此是首选。我们正在初始化的 int 没有任何需要右值的地方(如果我们改为初始化 int&&,那么 operator T&() const 就不会成为候选对象)。

首选returns T&的转换运算符,因为它比returns T&&的转换运算符更专业。

参见 C++17 [temp.deduct.partial]/(3.2):

In the context of a call to a conversion function, the return types of the conversion function templates are used.

和/9:

If, for a given type, deduction succeeds in both directions (i.e., the types are identical after the transformations above) and both P and A were reference types (before being replaced with the type referred to above): — if the type from the argument template was an lvalue reference and the type from the parameter template was not, the parameter type is not considered to be at least as specialized as the argument type; ...