C ++ оператор перегрузки преобразования для пользовательского типа в std :: string - PullRequest
13 голосов
/ 19 августа 2010

Я надеюсь, что кто-то сможет ответить, почему следующее не работает. Терпите меня, хотя, я все еще очень нуб ... Я просто не могу понять, почему следующее

using namespace std;
#include <string>
#include <iostream>

class testClass
{
public:
 operator char* () {return (char*)"hi";};
 operator int ()  {return 77;};
 operator std::string  () {return "hello";};
};

int main()
{
 char* c;
 int i;
 std::string s = "goodday";

 testClass t;

 c = t;
 i = t;
 s = t;

 cout<< "char: " << c << " int: " << i << " string: "<<s<<endl;

 return 0;
}

выдает ошибку времени компиляции:

myMain.cpp: In function ‘int main()’:
myMain.cpp:23: error: ambiguous overload for ‘operator=’ in ‘s = t’
/usr/include/c++/4.2.1/bits/basic_string.h:500: note: candidates are: std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
/usr/include/c++/4.2.1/bits/basic_string.h:508: note:                 std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]
/usr/include/c++/4.2.1/bits/basic_string.h:519: note:                 std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(_CharT) [with _CharT = char, _Traits = std::char_traits<char>, _Alloc = std::allocator<char>]

Если я не попытаюсь присвоить

s = t;

это работает.

Я часами пытался даже понять сообщение об ошибке, но больше всего меня удивляет то, что он работает для char *.

Я благодарен за любую подсказку. Спасибо! Markus

Ответы [ 5 ]

11 голосов
/ 19 августа 2010

Ошибка пытается объяснить, что ваше назначение "s = t", где s - это std::string, будет действительным, если t тоже будет std::string, или если t было[const] char*.Ваши операторы преобразования могут конвертировать t в любой из них, поэтому у компилятора нет основы для выбора одного из других ....

Вы можете однозначно устранить это, выбрав нужное преобразование:

s = t.operator std::string();
s = static_cast<std::string>(t);

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

Вы можете в конце концов обнаружить, что любой оператор преобразования доставляет больше хлопот, чемстоит ... говорит о том, что std::string сам по себе не предоставляет оператора преобразования в const char*.

9 голосов
/ 19 августа 2010

$ 13.3.1.5 / 2 состояния- "Рассматриваются функции преобразования S и его базовых классов. Те, которые не скрыты в S и дают тип T или тип, который можно преобразовать в тип T через стандартпоследовательность преобразования (13.3.3.1.1) является функциями-кандидатами. Считается, что функции преобразования, которые возвращают cv-квалифицированный тип, дают неквалифицированную cv версию этого типа для этого процесса выбора функций-кандидатов. Функции преобразования, которые возвращают «ссылку на cv2»X »возвращает lvalues ​​типа« cv2 X »и, следовательно, считается, что дает X для этого процесса выбора функций-кандидатов."

Назначение s = t работает следующим образом:

a) Рассматриваются все члены типа 't' (testClass), которые могут преобразовать 't' в 's'.

Candidate 1: operator string();   // s created using member string::operator=(string const&)
Candidate 2: operator char *()    // s created using member string::operator=(char const*)
Candidate 3: operator char *()    // s created using member string::operator=(char *)

b) Все вышеперечисленные кандидаты являются жизнеспособными (то есть вПри отсутствии других кандидатов компилятор может успешно разрешить вызов функции для любого из них)

c) Однако теперь лучше всего черезНужный кандидат должен быть определен.Используемые последовательности преобразования:

Candidate 1: U1 : operator string()
Candidate 2: U2 : operator char*()->const qualification to match string::operator=(char const*)
Candidate 3: U3 : operator char*()

$ 13.3.3.1.1 / 3 состояния - «Ранг последовательности преобразования определяется с учетом ранга каждого преобразования в последовательности и рангалюбая ссылочная привязка (13.3.3.1.4). Если любой из них имеет ранг конверсии, последовательность имеет ранг конверсии; "

Это означает, что все U1, U2 и U3 имеют ранг конверсии и впервый уровень не лучше другого.Однако в стандарте также указывается

Пользовательская последовательность преобразования U1 является лучшей последовательностью преобразования, чем другая пользовательская последовательность преобразования U2, если они содержат одну и ту же пользовательскую функцию преобразования или конструктор, и если втораястандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2.

Итак, давайте посмотрим, что это значит.

Между U1 и U2, они включают в себя различные функции преобразованияи, следовательно, ни один не лучше, чем другой

Между U1 и U3, они включают в себя различные функции преобразования, и, следовательно, ни один не лучше, чем другие

Так что насчет U1 и U2.Они включают в себя одну и ту же функцию преобразования, которая удовлетворяет первой части условия «и» выше

Так что насчет части », и если вторая стандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2. "

В U2 вторая стандартная последовательность преобразования требует квалификации const, тогда как в U3 это не требуется.Второй стандартной последовательностью преобразования U3 является точное совпадение.

Но, как указано в таблице 9 в стандартных состояниях, квалификация CV также считается точным соответствием.

Следовательно, U2 и U3 такжедействительно неразличимо с точки зрения разрешения перегрузки.

Это означает, что все U1, U2 и U3 действительно так же хороши, как и друг друга, и компилятор считает, что разрешение вызова (как часть оператора присваивания) является неоднозначным, посколькуне однозначно лучшее жизнеспособная функция

3 голосов
/ 19 августа 2010

Там нет точного std :: string :: operator =.Кандидаты, перефразируя,

s = (const std::string)(std::string)t;
s = (const char*)t;
s = (char)(int)t;

Я думаю, что все будет работать, если вы измените его, чтобы вернуть const std :: string. ( EDIT: I'mнеправильно.) Также обратите внимание, что первая функция должна возвращать const char *.Если вам нужно привести строковый литерал к char *, вы делаете что-то не так;строковые литералы не для записи.

1 голос
/ 19 августа 2010

На самом деле это потому, что std::string предлагает оператор присваивания, который принимает const char*.

0 голосов
/ 19 августа 2010

Хорошо, большое спасибо уже всем.Я думаю, что начинаю понимать, что-то вроде ...

Прежде всего, я не знал о том факте, что char - это просто 8-битный int.Спасибо за это разъяснение.

Итак, я понимаю, что для std :: string определены три оператора присваивания, каждый из которых имеет разные аргументы (string, char *, const char *) с правой стороны отМое выражение

s=t

не знает, в какой тип нужно преобразовать, поскольку существует несколько потенциально совпадающих (для этого присваивания std :: string) преобразований, определенных с

operator int ()  {return 77;};
operator std::string  () {return "hello";};

(начиная с char: 8bit int)

или

operator char* () {return (char*)"hi";};
operator std::string  () {return "hello";};

Это правильно?Таким образом, с точки зрения идиотов, левая часть задания не говорит правой стороне, какой тип он ожидает, поэтому rhs должен выбирать из своих вариантов, где один так же хорош, как другой?std :: string operator = терпимо относится к моим намерениям?

Пока все хорошо, я думал, что понял - но тогда почему следующее также создает двусмысленность?

 using namespace std;
 #include <string>
 #include <iostream>

 class testClass
  {
   public:
     operator float ()  {return float(77.333);};
     operator std::string  () {return "hello";};
  };

  int main()
  {
    std::string s = "goodday";
    testClass t;

    s = t;

    cout<< " string: "<<s <<endl;

    return 0;
  }

Теперь я определил только один соответствующий оператор преобразования, верно?Оператор std :: string = не может принимать числа с плавающей точкой, или так?Или float каким-то образом снова эквивалентен некоторому варианту char?

Я понимаю код как 's =', говорящий rhs: "дай мне строку, char * или const char *"

Rhs проверяет, что он может предоставить для экземпляра testClass, и единственное соответствие - testClass :: operator std :: string

Опять же, спасибо за ваше терпение, опыт и время, ребята - я действительно ценю это.

...