转换构造函数与转换运算符:优先级 [英] Conversion constructor vs. conversion operator: precedence

查看:231
本文介绍了转换构造函数与转换运算符:优先级的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这里阅读一些关于转换运算符和构造函数的问题让我想到了它们之间的交互,即当有一个模糊的调用。考虑下面的代码:

  A类; 

class B {
public:
B(){}

B(const A&)//转换构造函数
{
cout<< 称为B的转换构造函数< endl;
}
};

class A {
public:
operator B()//转换运算符
{
cout< 称为A的转换运算符< endl;
return B();
}
};

int main()
{
B b = A(); //这里应该叫什么?显然,A :: operator B()
return 0;
}

上述代码显示调用A的转换运算符,意味着转换运算符被称为与构造函数相对。如果从 A 中删除​​/注释掉运算符B()代码,编译器将乐意切换到使用构造函数(没有对代码的其他更改)。



我的问题是:


  1. 由于编译器不考虑 B b = A(); 是一个模糊的调用,在这里必须有一些类型的优先级。这个优先权究竟在哪里建立? (来自C ++标准的引用/引用将受到赞赏)

  2. 从面向对象的哲学观点来看,这是代码的行为方式吗?谁更了解如何 A 对象应该成为 B 对象, A B ?根据C ++,答案是 A - 在面向对象实践中有什么表明这应该是这种情况吗?对我个人来说,这是有道理的,所以我有兴趣知道如何做出选择。

提前感谢

解决方案

您执行复制初始化,并且被认为在转换序列中执行转换的候选函数是转换函数和转换构造函数。

  B(const A&)
操作符B()

现在,这是你声明它们的方式。重载解析抽象出来,并将每个候选转换成对应于调用参数的参数列表。参数为

  B(const A&)
B(A&)

第二个是因为转换函数是一个成员函数。 A& 是候选者是成员函数时生成的所谓隐式对象参数。现在,参数有 A 类型。当绑定隐式对象参数时,非const引用可以绑定到右值。所以,另一个规则说,当你有两个可行的函数的参数是引用,那么具有最少 const限定条件的候选人将获胜。这就是为什么你的转换功能赢了。尝试运算符B 一个const成员函数。你会注意到一个模糊。


从面向对象的哲学观点来看,这是代码的行为方式吗?谁知道更多关于A对象应该成为B对象,A还是B?根据C ++,答案是A - 在面向对象的实践有什么表明这应该是这种情况吗?对我个人来说,这是有道理的,所以我有兴趣知道如何做出选择。


,如果你使转换函数是一个const成员函数,那么GCC将选择构造函数(因此GCC似乎认为 B 有更多的业务?切换到教学模式( -pedantic )以使其导致诊断。






标准版, 8.5 / 14


否则剩余复制初始化情况),可以如13.3.1.4中所述枚举可以从源类型转换到目的地类型或(当使用转换函数时)到其派生类的用户定义的转换序列,


并且 13.3.1.4


重载解析用于选择要调用的用户定义转换。假设cv1 T是被初始化的对象的类型,T是类类型,候选函数选择如下:




  • T的转换构造函数(12.3.1)是候选函数。

  • 当初始化器表达式的类型是类类型cv S时,S的转换函数及其基类被考虑。没有隐藏在S中并且产生其cv非限定版本是与T相同类型或其派生类的类型是候选函数。返回引用X的转换函数返回类型X的左值,因此被认为在选择候选函数的过程中产生X.



在这两种情况下,参数列表都有一个参数,它是初始化表达式。 [注意:这个参数将与构造函数的第一个参数和转换函数的隐式对象参数进行比较。 ]


13.3.3.2/3

$ b $如果[...] S1和S2是引用绑定(8.5),则标准转换序列S1是比标准转换序列S2更好的转换序列。 .3),并且引用引用的类型除了顶级cv限定符之外是相同类型,并且由S2引用初始化的引用所针对的类型比通过初始化引用初始化的引用的类型更加cv限定S1表示。


Reading some questions here on SO about conversion operators and constructors got me thinking about the interaction between them, namely when there is an 'ambiguous' call. Consider the following code:

class A;

class B { 
      public: 
         B(){} 

         B(const A&) //conversion constructor
         { 
              cout << "called B's conversion constructor" << endl; 
         } 
};

class A { 
      public: 
         operator B() //conversion operator
         { 
              cout << "called A's conversion operator" << endl; 
              return B(); 
         } 
};

int main()
{
    B b = A(); //what should be called here? apparently, A::operator B()
    return 0;
}

The above code displays "called A's conversion operator", meaning that the conversion operator is called as opposed to the constructor. If you remove/comment out the operator B() code from A, the compiler will happily switch over to using the constructor instead (with no other changes to the code).

My questions are:

  1. Since the compiler doesn't consider B b = A(); to be an ambiguous call, there must be some type of precedence at work here. Where exactly is this precedence established? (a reference/quote from the C++ standard would be appreciated)
  2. From an object-oriented philosophical standpoint, is this the way the code should behave? Who knows more about how an A object should become a B object, A or B? According to C++, the answer is A -- is there anything in object-oriented practice that suggests this should be the case? To me personally, it would make sense either way, so I'm interested to know how the choice was made.

Thanks in advance

解决方案

You do copy initialization, and the candidate functions that are considered to do the conversions in the conversion sequence are conversion functions and converting constructors. These are in your case

B(const A&)
operator B() 

Now, that are the way you declare them. Overload resolution abstracts away from that, and transforms each candidate into a list of parameters that correspond to the arguments of the call. The parameters are

B(const A&)
B(A&)

The second one is because the conversion function is a member function. The A& is the so-called implicit object parameter that's generated when a candidate is a member function. Now, the argument has type A. When binding the implicit object parameter, a non-const reference can bind to an rvalue. So, another rule says that when you have two viable functions whose parameters are references, then the candidate having the fewest const qualification will win. That's why your conversion function wins. Try making operator B a const member function. You will notice an ambiguity.

From an object-oriented philosophical standpoint, is this the way the code should behave? Who knows more about how an A object should become a B object, A or B? According to C++, the answer is A -- is there anything in object-oriented practice that suggests this should be the case? To me personally, it would make sense either way, so I'm interested to know how the choice was made.

For the record, if you make the conversion function a const member function, then GCC will chose the constructor (so GCC seems to think that B has more business with it?). Switch to pedantic mode (-pedantic) to make it cause a diagnostic.


Standardese, 8.5/14

Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3).

And 13.3.1.4

Overload resolution is used to select the user-defined conversion to be invoked. Assuming that "cv1 T" is the type of the object being initialized, with T a class type, the candidate functions are selected as follows:

  • The converting constructors (12.3.1) of T are candidate functions.
  • When the type of the initializer expression is a class type "cv S", the conversion functions of S and its base classes are considered. Those that are not hidden within S and yield a type whose cv-unqualified version is the same type as T or is a derived class thereof are candidate functions. Conversion functions that return "reference to X" return lvalues of type X and are therefore considered to yield X for this process of selecting candidate functions.

In both cases, the argument list has one argument, which is the initializer expression. [Note: this argument will be compared against the first parameter of the constructors and against the implicit object parameter of the conversion functions. ]

And 13.3.3.2/3

  • Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.

这篇关于转换构造函数与转换运算符:优先级的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆