簡體   English   中英

Clang模糊與自定義轉換運算符

[英]Clang ambiguity with custom conversion operator

當我在clang下遇到問題時,我一直在開發一種適配器類。 如果定義了左值引用和右值引用的轉換運算符,則會在嘗試從類中移動時出現歧義編譯錯誤(當此類代碼應該沒問題時,

operator const T& () const&

只適用於左手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"
}

在gcc 4.9+上編譯正常,在任何clang版本上都失敗。 所以問題是:有沒有解決方法? 我對const& function修飾符的理解是對的嗎?

PS:澄清 - 問題是關於修復StringDecorator類本身(或找到這樣的類的解決方法,就好像是庫代碼)。 請不要直接提供調用操作符T &&()的答案或明確指定轉換類型。

問題來自於選擇最佳可行功能。 在第二個func調用的情況下,它意味着比較2個用戶定義的轉換序列 遺憾的是,如果不使用相同的用戶定義轉換函數或構造函數C ++標准 [over.ics.rank / 3],則無法區分 2個用戶定義的轉換序列

除非滿足以下規則之一,否則相同形式的兩個隱式轉換序列是無法區分的轉換序列:

  • [...]

  • 用戶定義的轉換序列U1是一個比另一個用戶定義的轉換序列U2更好的轉換序列,如果它們包含相同的用戶定義的轉換函數或構造函數[...]

因為rvalue總是可以綁定到const lvalue引用,所以如果函數為const std::string&std::string&&重載,你將無論如何都會陷入這種模糊的調用。

正如您所提到的,我正在重新聲明所有函數的第一個答案不是解決方案,因為您正在實現庫。 實際上,不可能為所有以string作為參數的函數定義代理函數!

因此,讓您在兩個不完美的解決方案之間進行權衡:

  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. } 

另一個解決方案是不使用Clang,因為它是Clang錯誤

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

Oliv的答案是正確的,因為在這種情況下標准似乎很清楚。 我一次選擇的解決方案是只留下一個轉換運算符:

operator const std::string& () const&

存在問題是因為兩個轉換運算符都被認為是可行的 因此,可以通過將lvalue轉換運算符的隱式參數的類型從const&更改為來避免這種情況:

operator const std::string& () & // lvalue only (rvalue can't bind to non-const reference)
{
    return m_string;
}

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

但這打破了const StringDecorator的轉換,使其在典型情況下的使用變得尷尬。

這個破碎的解決方案讓我想到是否有一種方法來指定成員函數限定符,它將使轉換操作符對於const lvalue對象可行,但不能與rvalue一起使用。 我已經設法通過將const轉換運算符的隱式參數指定為const volatile&來實現此目的:

operator const std::string& () const volatile& // lvalue only (rvalue can't bind to volatile reference)
{
    return const_cast< const StringDecorator* >( this )->m_string;
}

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

Per [dcl.init.ref] / 5 ,對於通過綁定到rvalue初始化的引用,引用必須是const非易失性左值引用或rvalue引用:

而左值引用和const左值引用可以綁定到const volatile引用。 顯然,成員函數上的volatile修飾符服務完全不同。 但是,嘿,它對我的​​用例來說是有效的。 唯一剩下的問題是代碼變得誤導和驚人。

clang ++更准確。 兩個func重載都不是StringDecorator const&StringDecorator&&完全匹配。 因此無法移動my_string 編譯器無法在可能的轉換之間進行選擇StringDecorator&& - > std::string&& - > func(std::string&&)StringDecorator&& - > StringDecorator& - > std::string& - > func(const std::string&) 換句話說,編譯器無法確定應該在哪個步驟應用強制轉換運算符。

我沒有安裝g ++來檢查我的假設。 我想這是第二種方式,因為無法移動my_string ,它將轉換運算符const&應用於StringDecorator& 如果將調試輸出添加到強制轉換運算符的主體中,則可以檢查它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM