2

I am coming up against a vexing conundrum in my code base. I can't quite tell why my code generates this error, but (for example) std::string does not.

class String {
public:
    String(const char*str);
    friend String operator+ ( const String& lval, const char *rval );
    friend String operator+ ( const char *lval, const String& rval );
    String operator+ ( const String& rval );
};

The implementation of these is easy enough to imagine on your own.

My driver program contains the following:

String result, lval("left side "), rval("of string");
char lv[] = "right side ", rv[] = "of string";
result = lv + rval;
printf(result);
result = (lval + rv);
printf(result);

Which generates the following error in gcc 4.1.2:

driver.cpp:25: error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
String.h:22: note: candidate 1: String operator+(const String&, const char*)
String.h:24: note: candidate 2: String String::operator+(const String&)

So far so good, right? Sadly, my String(const char *str) constructor is so handy to have as an implicit constructor, that using the explicit keyword to solve this would just cause a different pile of problems.

Moreover... std::string doesn't have to resort to this, and I can't figure out why. For example, in basic_string.h, they are declared as follows:

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT, _Traits, _Alloc>
operator+(const basic_string<_CharT, _Traits, _Alloc>& __lhs,
          const basic_string<_CharT, _Traits, _Alloc>& __rhs)

template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT,_Traits,_Alloc>
operator+(const _CharT* __lhs,
          const basic_string<_CharT,_Traits,_Alloc>& __rhs);

and so on. The basic_string constructor is not declared explicit. How does this not cause the same error I'm getting, and how can I achieve the same behavior??

4

5 に答える 5

9

あいまいさの理由は、あるパラメーターが他のパラメーターよりも不一致であるパラメーターがない場合にのみ、1つの候補関数が別の候補関数よりも優れているためです。2つの機能を検討してください。

friend String operator+(const String&, const char*); // (a)
String operator+(const String&);                     // (b)

とで呼び出しoperator+ています。 Stringconst char*

タイプの2番目の引数は、const char*(a)(b)よりも明らかに一致します。(a)と完全に一致しますが、(b)にはユーザー定義の変換が必要です。

したがって、あいまいさが存在するためには、最初の引数が(a)よりも(b)に一致する必要があります。

呼び出しのString左側にoperator+あるはconstではありません。したがって、これは、非定数メンバー関数である(b)と一致し、。を取る(a)よりも優れていますconst String&

したがって、次のソリューションのいずれかにより、あいまいさが解消されます。

  • operator+メンバーをconstメンバー関数に変更します
  • 非会員operator+を変更してString&const String&
  • operator+左側にconst文字列を使用して呼び出します

明らかに、UncleBensによって提案された最初のものが最善の方法です。

于 2010-04-10T16:35:58.073 に答える
5

It is sufficient in this case just to define on operator+:

String operator+(const String& lval, const String& rval);

Because you provide a constructor taking a char*, a String can be constructed from a char* during the call to operator+. For example:

String hello = "Hello, ";
const char* world = "world!";

String helloWorld = hello + world;

A temporary String will be constructed with the contents of the char* world (because your constructor is not explicit), then the two String objects will be passed to operator+.

于 2010-04-10T14:12:17.747 に答える
3

メンバー+ constを宣言すると、エラーはなくなります。

class String {
public:
    String(const char*str);
    friend String operator+ ( const String& lval, const char *rval );
    friend String operator+ ( const char *lval, const String& rval );
    String operator+ ( const String& rval ) const; //<-- here
};

しかし、その理由はわかりません。おそらく、可能であればconst参照よりもバインディング引数を優先するため、最初のオーバーロードは左側の値によく一致し、3番目のオーバーロードは右側の値によく一致します。

より良い説明。(問題を少し読み間違えたに違いありません。)


printf(result);

あなたの文字列が暗黙の変換を持っていると私に言わないでくださいconst char*...それは悪いことです。

于 2010-04-10T15:13:41.440 に答える
2

テンプレート関数と非テンプレート関数は、異なる規則に従います。テンプレート関数は、変換が適用されることなく、実際のパラメータータイプで選択されます。非テンプレート(つまりコード)の場合、暗黙の変換を適用できます。したがって、basic_stringのテンプレート化されたものはあいまいではありませんが、あなたのものはあいまいです。

于 2010-04-10T14:58:54.380 に答える
1

You've shown that basic_string has implementations of operator+ corresponding to the second and third operators in your class String. Does basic_string also have an operator corresponding to your first operator [friend String operator+ ( const String& lval, const char *rval );]?

What happens if you remove this operator?

于 2010-04-10T14:13:27.853 に答える