C ++ сложность неоднозначности перегрузки - PullRequest
2 голосов
/ 10 апреля 2010

Я сталкиваюсь с неприятной загадкой в ​​моей кодовой базе. Я не могу точно сказать, почему мой код генерирует эту ошибку, но (например) std :: string нет.

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 );
};

Внедрение этого достаточно легко представить самостоятельно.

Моя программа драйвера содержит следующее:

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);

, которая генерирует следующую ошибку в 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&)

Пока все хорошо, верно? К сожалению, мой конструктор String (const char * str) настолько удобен для использования в качестве неявного конструктора, что использование явного ключевого слова для решения этой проблемы может вызвать другие проблемы.

Более того ... std :: string не нужно прибегать к этому, и я не могу понять, почему. Например, в basic_string.h они объявлены следующим образом:

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);

и так далее. Конструктор basic_string не объявлен явным. Как это не вызывает ту же ошибку, которую я получаю, и как я могу добиться того же поведения ??

Ответы [ 5 ]

9 голосов
/ 10 апреля 2010

Причина неоднозначности заключается в том, что одна функция-кандидат лучше, чем другая функция-кандидат, только если нет ее параметров хуже, чем параметры другой. Рассмотрим две ваши функции:

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

Вы звоните operator+ с String и const char*.

Второй аргумент типа const char* явно соответствует (а) лучше, чем (б). Это точное совпадение для (a), но для (b) требуется определенное пользователем преобразование.

Таким образом, чтобы иметь место неоднозначность, первый аргумент должен соответствовать (b) лучше, чем (а).

String в левой части вызова на operator+ не является константой. Следовательно, он соответствует (b), который является неконстантной функцией-членом, лучше, чем (a), который принимает const String&.

Следовательно, любое из следующих решений устранит неоднозначность:

  • Измените элемент operator+ на постоянную функцию-член
  • Измените не члена operator+, чтобы взять String& вместо const String&
  • Вызов operator+ с константной строкой с левой стороны

Очевидно, что первый, , также предложенный UncleBens , - лучший путь.

5 голосов
/ 10 апреля 2010

В этом случае достаточно просто определить по operator+:

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

Поскольку вы предоставляете конструктор, принимающий char*, String может быть создан из char* во время вызова operator+. Например:

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

String helloWorld = hello + world;

Будет создан временный String с содержимым char* world (поскольку ваш конструктор не является явным), тогда два объекта String будут переданы operator+.

3 голосов
/ 10 апреля 2010

Ошибка исчезнет, ​​если вы объявите элемент + 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
};

Не уверен, в чем причина, хотя. Возможно, он предпочитает привязывать аргументы к константной ссылке, если это возможно, поэтому первая перегрузка лучше соответствует левому значению, а третья перегрузка лучше соответствует правому.

Лучшее объяснение. (Должно быть, неправильно прочитал проблему.)


printf(result);

Не говорите, что ваша строка имеет неявное преобразование в const char* ... Это зло.

2 голосов
/ 10 апреля 2010

Шаблонные и не шаблонные функции следуют различным правилам. Функции шаблона выбираются для фактических типов параметров без каких-либо преобразований. В случае не-шаблона (то есть вашего кода) может быть применено неявное преобразование. Таким образом, шаблонные вещи в basic_string не являются двусмысленными, но ваши есть.

1 голос
/ 10 апреля 2010

Вы показали, что basic_string имеет реализации operator+, соответствующие второму и третьему операторам в вашем классе String. У basic_string также есть оператор, соответствующий вашему первому оператору [friend String operator+ ( const String& lval, const char *rval );]?

Что произойдет, если вы удалите этот оператор?

...