简体   繁体   English

自定义类型转换的重载解析

[英]Overload resolution of user-defined type conversion

I considered a type conversion from one type to the other, which are defined by two way, ie, type conversion constructor and type conversion function.我考虑了从一种类型到另一种类型的类型转换,它通过两种方式定义,即类型转换构造函数和类型转换函数。

struct to_type;
struct from_type{
   operator to_type()const;
};
struct to_type{
   to_type() = default;
   to_type(const from_type&){}
};
from_type::operator to_type()const{return to_type();}

int main(){
    from_type From;
    to_type To;
    To = From;

    return 0;
}

gcc (v13.0.0, but seems to be same even in v4.9.4) don't throw any error and just call type conversion constructor in the above code. gcc(v13.0.0,但即使在 v4.9.4 中似乎也一样)不要抛出任何错误,只需在上面的代码中调用类型转换构造函数

On the other hand, clang (v16.0.0, but seems to be same even in v7.6.0) throw a "ambiguous" compile error like the following.另一方面,clang(v16.0.0,但即使在 v7.6.0 中似乎也相同)抛出如下所示的“模棱两可”编译错误。

prog.cc:14:10: error: reference initialization of type 'to_type &&' with initializer of type 'from_type' is ambiguous
    To = From;
         ^~~~
prog.cc:3:4: note: candidate function
   operator to_type()const;
   ^
prog.cc:7:4: note: candidate constructor
   to_type(const from_type&){}
   ^
prog.cc:5:8: note: passing argument to parameter here
struct to_type{
       ^
1 error generated.

It seems to be so curious that two major compiler show different result for this simple code.两个主要的编译器对这个简单的代码显示不同的结果似乎很奇怪。 Is either compiler don't match with the standard for C++?编译器是否不符合 C++ 标准? I guess this overload resolution related to [over.ics.rank] , but I could not concluded which compiler's behavior match to the standard.我猜这个重载决议与[over.ics.rank]相关,但我无法断定哪个编译器的行为符合标准。

Or do my source code contains undefined behavior?或者我的源代码是否包含未定义的行为?


[ADD 2022-12-21T00:20Z] [添加 2022-12-21T00:20Z]

Following the comment by Artyer, I tried -pedantic compile option for gcc, and now gcc also output error message.根据 Artyer 的评论,我尝试了 gcc 的-pedantic编译选项,现在gcc 也输出错误消息。

prog.cc: In function 'int main()':
prog.cc:14:10: error: conversion from 'from_type' to 'to_type' is ambiguous
   14 |     To = From;
      |          ^~~~
prog.cc:9:1: note: candidate: 'from_type::operator to_type() const'
    9 | from_type::operator to_type()const{return to_type();}
      | ^~~~~~~~~
prog.cc:7:4: note: candidate: 'to_type::to_type(const from_type&)'
    7 |    to_type(const from_type&){}
      |    ^~~~~~~
prog.cc:5:8: note:   initializing argument 1 of 'constexpr to_type& to_type::operator=(to_type&&)'
    5 | struct to_type{
      |        ^~~~~~~

This suggests that at least the default behavior of gcc without -pedantic don't match with the requirements of C++ standard for this source code.这表明至少没有-pedantic的 gcc 的默认行为不符合 C++ 标准对此源代码的要求。

In this case clang is right, this behaviour is defined in[over.best.ics.general] , and standard even mentions that such a conversion is ambiguous explicitly under the sample code to [over.best.ics.general]/10 (the scenario under the link is in fact considers another kind of ambiguity, but resolution to user-defined pair of conversion constructor and conversion operator is one of the candidates, so I removed the part of the code with another candidate):在这种情况下 clang 是正确的,这种行为在[over.best.ics.general]中定义,标准甚至提到这种转换在示例代码下明确地不明确[over.best.ics.general]/10 (链接下的场景其实是考虑了另一种歧义,但是解析到用户定义的一对转换构造函数和转换运算符是候选之一,所以我删除了与另一个候选的部分代码):

class B;
class A { A (B&);};
class B { operator A (); };
...
void f(A) { }
...
B b;
f(b); // ... an (ambiguous) conversion b → A (via constructor or conversion function)

In order to break the name resolution down, I'd like to represent the conversion sequence ( To = From; ) as an assignment operator function:为了分解名称解析,我想将转换序列 ( To = From; ) 表示为赋值运算符函数:

to_type& operator=(to_type&& param) { } // defined implicitly by the compiler

The actual conversion happens when param needs to get into existence out of From argument of type from_type .param需要从from_type类型的From参数中产生时,就会发生实际的转换。 The compiler then needs to decide step by step:然后编译器需要一步步决定:

  1. Which type the conversion sequence from from_type to to_type&& is of?from_typeto_type&&的转换顺序是什么类型的? ( standard / user defined / ellipsis ): 标准/用户定义/省略号):

    • The types from_type is user defined, but to_type&& is of reference type, and reference binding could be considered identity (ie standard) conversion. from_type是用户定义的类型,而to_type&&是引用类型,引用绑定可以被认为是身份(即标准)转换。 However it's not the case, since from_type and to_type are not the same and cannot be bound directly ( [over.ics.ref]/2 ):然而事实并非如此,因为from_typeto_type不一样,不能直接绑定( [over.ics.ref]/2 ):

    When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the referenced type according to [over.best.ics] .当引用类型的参数未直接绑定到参数表达式时,转换序列是根据[over.best.ics]将参数表达式转换为引用类型所需的序列。

    • Without reference binding there is no any other standard conversion sequence that may suit here.没有引用绑定,就没有任何其他标准转换序列可能适合这里。 Let's consider user-defined conversion .让我们考虑用户定义的转换 [over.ics.user] gives us the following definition: [over.ics.user]给了我们以下定义:

    A user-defined conversion sequence consists of an initial standard conversion sequence followed by a user-defined conversion ( [class.conv] ) followed by a second standard conversion sequence.用户定义的转换序列由初始标准转换序列、紧随其后的用户定义转换 ( [class.conv] ) 和第二个标准转换序列组成。 If the user-defined conversion is specified by a constructor ( [class.conv.ctor] ), the initial standard conversion sequence converts the source type to the type of the first parameter of that constructor.如果用户定义的转换由构造函数 ( [class.conv.ctor] ) 指定,则初始标准转换序列会将源类型转换为该构造函数的第一个参数的类型。 If the user-defined conversion is specified by a conversion function , the initial standard conversion sequence converts the source type to the type of the implicit object parameter of that conversion function.如果用户定义的转换由转换函数指定,则初始标准转换序列将源类型转换为该转换函数的隐式对象参数的类型。

    This sounds about right to me: we need to convert from_type argument to to_type temporary in order for to_type&& to bind to the argument, thus the sequence is either这听起来对我来说是正确的:我们需要将from_type参数临时转换为to_type以便to_type&&绑定到参数,因此序列是

    • from_type -> const from_type& for the converting constructor argument to_type(const from_type&) -> to_type&& for the move-assignment operator of to_type& operator=(to_type&&) from_type -> const from_type&对于转换构造函数参数to_type(const from_type&) -> to_type&&对于to_type& operator=(to_type&&)的移动赋值运算符

    OR要么

    • from_type -> implicit-object-parameter of type const from_type -> to_type&& for the move-assignment operator of to_type& operator=(to_type&&) . from_type -> const from_type -> to_type&&用于to_type& operator=(to_type&&)的移动赋值运算符
  2. Now we have two possible conversion sequences of the same kind ( user-defined ).现在我们有两个可能的相同类型(用户定义)的转换序列。 For this scenario [over.best.ics.general]/10 says the following:对于这种情况[over.best.ics.general]/10说明如下:

If there are multiple well-formed implicit conversion sequences converting the argument to the parameter type, the implicit conversion sequence associated with the parameter is defined to be the unique conversion sequence designated the ambiguous conversion sequence.如果有多个格式良好的隐式转换序列将实参转换为形参类型,则与参数关联的隐式转换序列被定义为唯一的转换序列,指定为歧义转换序列。 For the purpose of ranking implicit conversion sequences as described in [over.ics.rank] .用于对[over.ics.rank]中描述的隐式转换序列进行排名。

The Ranking implicit conversion sequences documentation then gives the following clues about deciding on which conversion (of the same sequence type) should take precedence for user-defined sequences ( [over.ics.rank]/3.3 , emphasis mine):然后, Ranking implicit conversion sequences文档提供了以下线索,用于决定哪种转换(相同序列类型)应优先于用户定义的序列( [over.ics.rank]/3.3 ,强调我的):

User-defined conversion sequence U1 is a better conversion sequence than another user-defined conversion sequence U2 if they contain the same user-defined conversion function or constructor or they initialize the same class in an aggregate initialization and in either case the second standard conversion sequence of U1 is better than the second standard conversion sequence of U2用户定义的转换序列U1是比另一个用户定义的转换序列U2更好的转换序列,如果它们包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化相同的类,并且在任何一种情况下都是第二个标准转换序列U1的第二个标准转换序列优于U2

Here we go, for both scenarios (with the converting constructor and the conversion function) the second standard conversion is of the same type (a temporary of type to_type to to_type&& ), thus the operations are indistinguishable.在这里,对于这两种情况(使用转换构造函数和转换函数),第二个标准转换是相同的类型(临时类型to_typeto_type&& ),因此操作是无法区分的。

Clang is wrong in rejecting the program because an overload with T&& is better match than an overload with const int& . Clang 拒绝该程序是错误的,因为使用T&&的重载比使用const int&的重载更匹配。 Note that gcc is also wrong because it uses the copy constructor instead of operator to_type() const .请注意,gcc 也是错误的,因为它使用复制构造函数而不是operator to_type() const See demo .演示 Only msvc is right and both gcc and clang are wrong .只有msvc是对的,gcc和clang都是错的 MSVC correctly uses the conversion function. MSVC 正确使用了转换函数。

S1    S2
int   int&&         indistinguishable
int   const int&    indistinguishable
int&& const int&    S1 better

Consider the contrived example:考虑人为的例子:

#include <iostream>
struct to_type;
struct from_type{
   operator to_type()const;
};
struct to_type{
   to_type() = default;
   to_type(const from_type&){std::cout <<"copy ctor";}
};
from_type::operator to_type()const{
    std::cout<<"to_type operator";
    return to_type();}

void f(to_type&&){}

int main(){
    from_type From; 
    f(From); //valid and this should use from_type::operator to_type() const

}

Demo演示

The above program is rejected by clang(with the same error as you're getting) but accepted by gcc and msvc.上面的程序被 clang 拒绝(与你得到的错误相同)但被 gcc 和 msvc 接受。 Note that even though gcc accepts the above program it is still wrong because the conversion function should be used instead of the copy ctor.请注意,即使 gcc 接受上述程序,它仍然是错误的,因为应该使用转换函数而不是复制 ctor。 MSVC on the other hand correctly uses the conversion function.另一方面,MSVC 正确地使用了转换函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM