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

Oli*_*liv 6 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 
}
Run Code Online (Sandbox Code Playgroud)

与我错误的分析不同,编译器同意:调用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 
  }
Run Code Online (Sandbox Code Playgroud)

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

  • Clang(3.4.1至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中的描述对隐式转换序列进行排序,模糊转换序列被视为用户定义的转换序列,与任何其他用户定义的转换序列无法区分

Bar*_*rry 4

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

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
Run Code Online (Sandbox Code Playgroud)

对于第一次调用,我们倾向于使用 less cv限定的类型,因此int&比 更好int const&。对于第二次调用,我们更倾向于绑定到右值引用而不是绑定到左值引用,因此int&&int const&.

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

我称其为 clang bug。