【问题标题】:Understanding overload resolution ranking involving user defined conversion了解涉及用户定义转换的重载解决排名
【发布时间】:2018-05-22 14:48:49
【问题描述】:

我正在尝试了解重载分辨率

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

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 to 5.0), MSVC 19 2017 RTW, Zapcc 190308 (Clang derivate), ellcc (0.1.33, 0.1.34)(Clang derivate) => 对g的调用是不明确的;
  • GCC(4.8.1 到 7.2)、icc(16 到 18)=> 对 g 的调用没有歧义

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


您能否详细说明为什么以下规则不适用,或者何时适用?

[over.best.ics]/10:

如果存在多个不同的转换序列,每个序列都将实参转换为参数类型,则 与参数关联的隐式转换序列被定义为唯一的转换序列 指定的 模棱两可的转换序列 .为了将隐式转换序列排序为 在 16.3.3.2 中描述的模糊转换序列被视为用户定义的转换序列 这与任何其他用户定义的转换序列没有区别

【问题讨论】:

  • 您应该提及您使用的编译器版本和命令行选项。

标签: c++ overloading language-lawyer


【解决方案1】:

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

void f(int& );        // #1
void f(int const& );  // #2
void g(int&& );       // #3
void g(int const& );  // #4

int i;
f(i); // calls #1
g(0); // calls #3

对于第一次调用,我们支持less cv-qualified type,因此int& 优于int const&。对于第二次调用,我们更喜欢 binding to rvalue reference 而不是绑定到左值引用,所以 int&&int const& 更好。

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

我将其称为 clang 错误。

【讨论】:

  • 谢谢你的回答,但还是有问题……为什么规则 [over.best.ics]/10 不适用?我认为对于 f#2,存在两个替代转换序列的事实导致 f#2 的调用被视为使用 单个 用户定义的转换序列,该转换序列与用户定义的无法区分f#1 的转换。 g#2 和 g#1 的同上?
  • @Oliv 不明确的转换序列发生在我们有多个转换序列且没有一个最佳转换序列时。这不是这里的情况。
【解决方案2】:

N4713 16.3.3.2:

3.3 如果用户定义的转换序列 U1 包含相同的用户定义的转换函数或构造函数,或者它们在聚合初始化中初始化相同的类并且在无论哪种情况,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),两者都没有引用非静态成员函数的隐式对象参数,声明时没有引用限定符,S1 将右值引用绑定到右值,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-qualifiers 之外是相同的类型,并且 S2 初始化的引用所引用的类型比 cv-qualified 更多由 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 符合标准的要求。

【讨论】:

  • 谢谢你的回答,但还是有问题……为什么规则 [over.best.ics]/10 不适用?我认为对于 f#2,存在两个替代转换序列的事实导致 f#2 的调用被视为使用 单个 用户定义的转换序列,该转换序列与用户定义的无法区分f#1 的转换。 g#2 和 g#1 的同上?
猜你喜欢
  • 2014-04-10
  • 1970-01-01
  • 1970-01-01
  • 2021-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-14
相关资源
最近更新 更多