2010-04-10 4 views
2

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

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 не объявляется явно. Как это не вызывает ту же ошибку, которую я получаю, и как я могу достичь такого же поведения?

ответ

9

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

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

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

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

Следовательно, для того чтобы быть двусмысленностью, первый аргумент должен соответствовать (b) лучше, чем (a).

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

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

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

Очевидно, что первый, also suggested by UncleBens, - лучший способ пойти.

+0

Мне даже не приходило в голову, что константа будет причиной плохого матча. Вы и UncleBens были (конечно) в точности. Еще одно решение состоит в том, чтобы сделать член нечленом, который принимает две константные строки - на самом деле это почему версия std :: string не имеет ошибки. Конечно, это эквивалентно вашему первому предложению, просто написанному по-разному. – Nate

5

Достаточно в этом случае просто определить на 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+.

+0

Это создает бесполезный временный объект, возможно, со значительными затратами времени и памяти. –

+0

@ Даниэль Ньюби: Не совсем так, поскольку компилятор может ускользнуть от копирования довольно часто. Также, что «значительная стоимость времени и памяти» - это именно то, что делает 'std :: string', как задал OP в своем оригинальном посте. –

1

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

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

2

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

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

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

Better explanation. (Должно быть неправильно неполадке немного.)


printf(result); 

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

+4

Надеюсь, это нестандартная функция printf, найденная через ADL. Хотя это должно быть 'puts', а не' printf', если все это делается, это напечатать аргумент. –

+0

'puts' добавляет новую строку. Я предполагаю, что это должно быть 'printf ("% s ", str.c_str());' если новая строка не нужна. – UncleBens

+1

+1 Это, возможно, лучший вариант. Я попытался объяснить двусмысленность: http://stackoverflow.com/questions/2613645/c-addition-overload-ambiguity/2614085#2614085 –

Смежные вопросы