9

次のプログラムがあるとします。

#include <iostream>
#include <string>
using namespace std;
struct GenericType{
   operator string(){
      return "Hello World";
   }
   operator int(){
      return 111;
   }
   operator double(){
      return 123.4;
   }
};
int main(){
   int i = GenericType();
   string s = GenericType();
   double d = GenericType();
   cout << i << s << d << endl;
   i = GenericType();
   s = GenericType(); //This is the troublesome line
   d = GenericType();
   cout << i << s << d << endl;
}

Visual Studio 11でコンパイルされますが、clangやgccではコンパイルされません。暗黙的にaからaGenericTypeに変換したいので問題が発生しますが、aを返す可能性もあるため、あいまいさがあります(両方が一致します)。intcharstringoperator=(char)operator=(string)GenericType

ただし、コピーコンストラクターは問題ありません。

私の質問は、mainの内容を変更せずに、このあいまいさを解決するにはどうすればよいですか?GenericTypeこの状況を処理するために変更するには何をする必要がありますか?

4

4 に答える 4

10

gccとclangは正しいと思います。

operator=プレイには2つのオーバーロードがあります。

string& operator=(string const& str); // (1)
string& operator=(char ch);           // (2)

これらのoperator=オーバーロードは両方とも、タイプの引数からのユーザー定義の変換を必要としますGenericType(1)への変換を使用する必要がありますstring(2)intは、への変換と、それに続くへの標準変換を使用する必要がありますchar

重要なことは、両方のオーバーロードがユーザー定義の変換を必要とすることです。これらの変換の1つが他の変換よりも優れているかどうかを判断するために、過負荷解決ルール、具体的にはC++11§13.3.3.2/3の次のルール(わかりやすくするために再フォーマット)を確認できます。

ユーザー定義の変換シーケンスU1は、別のユーザー定義の変換シーケンスよりも優れた変換シーケンスU2です。

  1. それらには、同じユーザー定義の変換関数またはコンストラクターまたは集約初期化が含まれ、

  2. の2番目の標準変換シーケンスは、の2番目の標準変換シーケンスU1よりも優れていU2ます。

とはルールの2つの部分を結合するため、両方の部分が満たされる必要があることに注意してください。ルールの最初の部分は満たされていません。2つのユーザー定義の変換シーケンスは、異なるユーザー定義の変換関数を使用します。

したがって、どちらの変換も優れておらず、呼び出しはあいまいです。

[の定義を変更せずに問題を修正する方法についての良い提案はありませんmain()。暗黙の変換は通常、良い考えではありません。これらは非常に便利な場合もありますが、多くの場合、過負荷のあいまいさやその他の奇妙な過負荷動作を引き起こす可能性があります。]

この問題が説明され、設計どおりに解決されたgccバグレポートがありました。 コンパイラは、あいまいな演算子のオーバーロードを誤って診断します。

于 2012-05-24T18:29:56.860 に答える
2

GCCは間違っていると思います。BjarneStroustrupの著書「TheC++Programming Language」には、演算子のオーバーロードに関する章全体があります。セクション11.4.1の最初の段落は、次のように述べています。

「代入演算子X::operator =(Z)があり、VがZであるか、VからZへの一意の変換がある場合、クラスXのオブジェクトへの型Vの値の代入は有効です。初期化が処理されます。同等に。」

あなたの例では、GCCは「strings = GenericType();」を受け入れます。それでも"s= GenericType();"を拒否するため、割り当てを初期化と同じように処理していないことは明らかです。それが私の最初の手がかりでした。GCCで何かがおかしいのです。

GCCは、割り当てでGenericTypeを文字列に変換するための3つの候補を、すべてbasic_string.hで報告します。1つは正しい変換であり、1つは無効であると報告し、3つ目はあいまいさを引き起こします。これは、basic_string.hの演算子のオーバーロードであり、あいまいさを引き起こします。

/**
*  @brief  Set value to string of length 1.
*  @param  c  Source character.
*
*  Assigning to a character makes this string length 1 and
*  (*this)[0] == @a c.
*/
basic_string& operator=(_CharT __c) { 
    this->assign(1, __c); 
    return *this;
}

渡されるオブジェクトのタイプと一致しないオペランドを受け入れるため、これは有効な変換ではありません。charへの割り当てが試みられるような場所はありません。したがって、この変換は、あいまいさを引き起こすものよりもはるかに少ない候補であってはなりません。GCCは、テンプレートタイプとそのメンバーのオペランドタイプを混同しているようです。

編集:整数を文字列に割り当てることができるcharに変換できるため、文字列に整数を割り当てることが実際に合法であることに気づいていませんでした(ただし、文字列をcharに初期化することはできません!) 。GenericTypeは、intへの変換を定義するため、このメンバーを有効な候補にします。ただし、これは有効な変換ではないと私は主張します。この変換を使用すると、最初にGenericTypeからintに、次にintからstringに、割り当てに対して2つのユーザー定義の暗黙的な変換が発生するためです。同じ本11.4.1で述べられているように、「ユーザー定義の暗黙の変換の1つのレベルのみが合法です」。

于 2012-05-30T21:00:17.477 に答える
2

私の質問は、mainの内容を変更せずに、このあいまいさを解決するにはどうすればよいですか?

あいまいさのない名前の独自のクラスを作成stringし、あいまいさを持たないようにoperator=usingますstd

明らかに、これはあまり良い解決策ではありmainませんが、機能し、変更する必要はありません。

私はあなたが他の方法であなたが望む振る舞いを得ることができないと思います。

于 2012-06-03T12:42:54.487 に答える
1

このソリューションは機能します

#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
struct GenericType{

   operator string(){
      return "Hello World";
   }
   
   template <typename T, typename = std::enable_if_t <
                                    std::is_same<T, double>::value ||
                                    std::is_same<T, int>::value>>
   operator T(){
      return 123.4;
   }
};
int main(){
   int i = GenericType();
   string s = GenericType();
   double d = GenericType();
   cout << i << s << d << endl;
   i = GenericType();
   s = GenericType();
   d = GenericType();
   cout << i << s << d << endl;
}

そして、より一般的な解決策。暗黙の変換でうまくいくので、算術型ごとに演算子を作成する必要はないと思います。

// ...

template <typename T, typename = std::enable_if_t 
    <std::is_arithmetic<T>::value && !std::is_same<T, char>::value>>
operator T() const
{
    return std::stod("123.4");
}

//...
于 2021-06-19T20:50:21.170 に答える