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


27

考虑下面的代码

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]并没有帮助我弄清楚为什么后者更好。

为什么后一种功能比前一种更好?


我是一只简单的猫,我想说它肯定是第二只了,而另一只猫的引入将是C ++ 11中的重大变化。它会在某处成为标准。不过好问题,请投票。(注意专家:如果此评论无可厚非,请告诉我!)
Bathsheba

3
FWIW,template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;让它调用第一个。
NathanOliver

5
@LightnessRaceswithMonica转换运算符允许它,否则将无法拥有多个转换运算符。
NathanOliver

1
@Bathsheba我不认为这就是原因。这就像说不能通过重载决议选择move构造函数,因为这将是一项重大更改。如果编写移动构造函数,则表明您的代码被该定义“破坏”。
布莱恩

1
@LightnessRaceswithMonica我将其保持直觉的方式是将返回类型视为函数的名称。不同的类型等于不同的名称等于成功:)
NathanOliver

Answers:


13

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

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

在调用转换函数的上下文中,使用转换函数模板的返回类型。

和/ 9:

如果对于给定类型,推导在两个方向上都成功(即,在上面的转换之后,这些类型是相同的),并且在两个方向上都成功P并且A是引用类型(在被上述类型替换之前):—如果参数模板中的类型是左值引用,而参数模板中的类型不是,则不认为参数类型至少与参数类型一样专业;...


12

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

之间作出决定时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用于确定排序的类型取决于完成部分排序的上下文:

[SNIP]

(3.2)在调用转换函数的上下文中,使用转换函数模板的返回类型。

然后在选择重载时最终取决于“更专业的”规则:

(9.1)如果参数模板的类型是左值引用,而参数模板的类型不是左值引用,则不认为参数类型至少与参数类型一样专业;除此以外,

因此operator T&&至少operator T&没有专门的程度,同时没有规则的国家operator T&至少没有专门的程度operator T&&,因此operator T&比规则更专门operator T&&

更加专业的模板可以使过载解决方案胜过更少的解决方案,其他所有条件都一样。


我回答时有人添加了language-lawyer标签;我可以简单地删除。我有一段模糊的记忆,要阅读说明该参数被视为选择调用哪个参数的文本,以及讨论在选择模板重载时如何处理&&vs 的文本&,但要弄清楚确切的文本需要一段时间。 。
Yakk-Adam Nevraumont

4

我们正试图初始化一个intany。其过程:

  1. 弄清楚我们可以做到的所有方式。也就是说,确定我们所有的候选人。这些来自非显式转换函数,可以int通过标准转换序列([over.match.conv])进行转换。本节包含以下短语:

    对返回“对的引用X” 的转换函数的调用是type的glvalue,X因此,这种转换函数被认为产生X了选择候选函数的过程。

  2. 选择最佳人选。

步骤1之后,我们有两个候选人。operator int&() constoperator int&&() const,两者均int出于选择候选函数的目的而产生。哪个是最佳候选人int

我们确实有一个决胜局,它偏爱左值引用而不是右值引用([over.ics.rank] /3.2.3)。不过,我们在这里并没有真正绑定一个引用,该示例有些倒置-这是在参数是左值vs右值引用的情况下。

如果那不适用,那么我们会倾向于[over.match.best] /2.5决胜局,他们倾向于使用更专业的功能模板。

一般来说,经验法则是更具体的转换才是最佳匹配。左值引用转换功能比转发引用转换功能更具体,因此是首选。int我们正在初始化的东西并不需要一个右值(如果我们正在初始化int&&,那么那operator T&() const将不是候选者)。


3.2.3实际上说T&&胜利不是吗?啊,但是我们没有绑定右值。还是我们?在挑选候选人时,必须用一些词来定义我们的获得方式int&int&&,这是否具有约束力?
Yakk-Adam Nevraumont

@ Yakk-AdamNevraumont不,有人更喜欢左值引用而不是右值引用。我认为那可能是错误的部分,“更专业”的决胜局是正确的。
巴里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.