Clang模糊与自定义转换运算符

Ton*_*lov 6 c++ move decorator c++11

当我在clang下遇到问题时,我一直在开发一种适配器类.如果定义了左值引用和右值引用的转换运算符,则会在尝试从类中移动时出现歧义编译错误(当此类代码应该没问题时,

operator const T& () const&
Run Code Online (Sandbox Code Playgroud)

只适用于左手AFAIK).我用简单的例子重现了错误:

#include <string>

class StringDecorator
{
public:
  StringDecorator()
  : m_string( "String data here" )
  {}

  operator const std::string& () const& // lvalue only
  {
    return m_string;
  }

  operator std::string&& () && // rvalue only
  {
    return std::move( m_string );
  }

private:
    std::string m_string;
};

void func( const std::string& ) {}
void func( std::string&& ) {}

int main(int argc, char** argv)
{
  StringDecorator my_string;

  func( my_string ); // fine, operator std::string&& not allowed
  func( std::move( my_string ) ); // error "ambiguous function call"
}
Run Code Online (Sandbox Code Playgroud)

在gcc 4.9+上编译正常,在任何clang版本上都失败.所以问题是:有没有解决方法?我对const& function修饰符的理解是对的吗?

PS:澄清 - 问题是关于修复StringDecorator类本身(或找到这样的类的解决方法,就好像是库代码).请不要直接提供调用操作符T &&()的答案或明确指定转换类型.

Oli*_*liv 3

问题来自于最佳可行函数的选择。在第二次调用的情况下func,它意味着比较2个用户定义的转换序列。不幸的是,如果两个用户定义的转换序列不使用相同的用户定义转换函数或构造函数C++ 标准[over.ics.rank/3],则它们是无法区分的:

相同形式的两个隐式转换序列是不可区分的转换序列,除非适用以下规则之一:

  • [...]

  • 如果用户定义的转换序列 U1 包含相同的用户定义的转换函数或构造函数,则它们是比另一个用户定义的转换序列 U2 更好的转换序列 [...]

因为右值总是可以绑定到 const 左值引用,所以如果函数重载于const std::string&and ,您在任何情况下都会遇到这种不明确的调用std::string&&

正如您所提到的,我的第一个答案是重新声明所有函数,这并不是一个解决方案,因为您正在实现一个库。事实上,不可能为所有以 astring作为参数的函数定义代理函数!

这样您就可以在两个不完美的解决方案之间进行权衡:

  1. 您删除operator std::string&&() &&,您将失去一些优化,或者;

  2. 您公开继承自 std::string,并删除 2 个转换函数,在这种情况下,您的库会被滥用:

    #include <string>
    
    class StringDecorator
      : public std::string
    {
    public:
      StringDecorator()
      : std::string("String data here" )
      {}
    };
    
    void func( const std::string& ) {}
    void func( std::string&& ) {}
    
    int main(int argc, char** argv)
    {
      StringDecorator my_string;
    
      func( my_string ); // fine, operator std::string&& not allowed
      func( std::move( my_string  )); // No more bug:
        //ranking of standard conversion sequence is fine-grained.
    }
    
    Run Code Online (Sandbox Code Playgroud)

另一种解决方案是不使用 Clang,因为它是Clang 的一个错误

但如果你必须使用 Clang,Tony Frolov 的答案就是解决方案。