了解涉及用户定义转换的重载决策排名

时间:2017-12-08 10:58:34

标签: c++ overloading language-lawyer

我正在尝试了解重载解析

首先让我们考虑第一种情况:

struct int1{
   int val;
   operator int&()&{
      return val;
      }
   operator const int &() const&{
      return val;
      }
    };

void f(int &){}       //f#1
void f(const int&){}  //f#2

void test1(){
  int1 x;
  f(x);
   //Conversion sequence for f#1: 
   //   - int_wrapper& --> int1::operator int&
   //   => Ranking: user defined conversion rank
   //Converison sequence for f#2:
   //   - int1& --> int1::operator int & --> const int&
   //   - int1& --> const int1 &         --> int1::operator const int& 
   //   => Ranking: ambiguous because 2 conversion sequence [over.best.ics]/10 
   //            => user defined conversion rank 
   //
   //=> No best viable overload, 2 user defined conversion rank 
}

与我的错误分析不同,编译器同意:f的调用不明确。为什么呢?

现在考虑第二种情况,这非常相似,我只是将int&替换为int &&

struct int2{
   int val;
   operator int&&()&&{
      return std::move(val);
      }
   operator const int &() const&{
      return val;
      }
    };

void g(int &&){}     // g#1 
void g(const int&){} // g#2

void test2(){
  int2 x;
  g(std::move(x));
  //Conversions sequence for g#1 
  //   - int2&& --> int2::operator int&&
  //   => Ranking: user defined conversion rank
  //Conversion sequence for g#2 
  //   - int2&& --> const int2&           --> int2::operator const int&
  //   - int2&& --> int2::operator int&&  --> const int&
  //   => Ranking: ambiguous because 2 conversion sequence [over.best.ics]/10 
  //            => user defined conversion rank 
  //
  //=> No best viable overload, 2 user defined conversion rank 
  }

我的分析(在这种情况下肯定是错误的)也得出类似结论,对g的调用是模棱两可的。不幸的是,在第二种情况下,编译器不同意:

  • Clang(3.4.1至5.0),MSVC 19 2017 RTW,Zapcc 190308(Clang衍生物),ellcc(0.1.33,0.1.34)(Clang衍生物)=>致电g 含糊不清;
  • GCC(4.8.1至7.2),icc(16至18)=>致电g 不含糊

什么是正确的分析,哪个编译器是对的?

您能否确定以下规则不适用的原因,或何时适用?

[over.best.ics] / 10:

  

如果存在几个不同的转换序列,每个转换序列都将参数转换为参数类型,那么   与参数相关联的隐式转换序列被定义为唯一转换序列   指定了   模糊转换序列   。为了将隐式转换序列排序为   在16.3.3.2中描述的,模糊转换序列被视为用户定义的转换序列   与任何其他用户定义的转换序列无法区分

2 个答案:

答案 0 :(得分:3)

这两个例子都是更简单的基本概念的更复杂的表示:

int&

对于第一个电话,我们支持here.,因此int const&优于int&&。对于第二个调用,我们赞成less cv-qualified type过度绑定到左值引用,因此int const&优于int1&

在特定示例中,这些首选项表现为通过隐式对象参数使用的转换函数的选择。对于第一个示例,因为我们将隐式对象参数绑定到int&以转换为int1 const&,但int const&转换为int2&&。同样,在第二个示例中,我们将隐式对象参数绑定到int&&以转换为int2 const&,但int const&转换为{{1}}。

我称之为铿锵声。

答案 1 :(得分:1)

N4713 16.3.3.2:

  

3.3   用户定义的转换序列U1是一个更好的转换序列,如果它们包含相同的用户定义转换函数或构造函数,或者它们在聚合初始化中初始化同一个类,则它是另一个用户定义的转换序列U2在任何一种情况下,U1的第二个标准转换序列优于U2的第二个标准转换序列。   [实施例

struct A {
    operator short();
} a;
int f(int);
int f(float);
int i = f(a); // calls f(int), because short→int is // better than short →float
  

结束例子]

因此,标准转化是关键:

  

<强> 3.2   标准转换序列S1是比标准转换序列S2更好的转换序列,如果

     

<强> 3.2.3   S1和S2是引用绑定(11.6.3),并且都不引用没有ref-qualifier声明的非静态成员函数的隐式对象参数,S1将rvalue引用绑定到rvalue,S2绑定左值引用[示例:

int i;
int f1();
int&& f2();
int g(const int&);
int g(const int&&);
int j = g(i); // calls g(const int&)
int k = g(f1()); // calls g(const int&&)
int l = g(f2()); // calls g(const int&&)
struct A {
    A& operator<<(int);
    void p() &;
    void p() &&;
};
A& operator<<(A&&, char);
A() << 1; // calls A::operator<<(int)
A() << ’c’; // calls operator<<(A&&, char)
A a;
a << 1; // calls A::operator<<(int)
a << ’c’; // calls A::operator<<(int)
A().p(); // calls A::p()&&
a.p(); // calls A::p()&
  

结束例子]

     

<强> 3.2.6 :   S1和S2是引用绑定(11.6.3),引用引用的类型除了顶级cv限定符之外是相同的类型,并且S2引用的引用引用的类型比cv更合格。由S1初始化的引用所引用的类型。   [例如:

int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
int k = g(i); // ambiguous
struct X {
    void f() const;
    void f();
};
void g(const X& a, X b) {
    a.f(); // calls X::f() const
    b.f(); // calls X::f()
}
  

- 结束示例]

所以gcc符合标准的要求。