Избегайте конфликта операторов приведения () и оператора доступа []

Я был уверен, что это нужно спросить раньше, но не нашел.

У меня есть класс, инкапсулирующий std :: string, внутри я хочу перегрузить оба приведенных типа в char * и оператор доступа [].

class String
{
public :
    String(const char* s) { m_str = s; }
    const char* str() const  { return m_str.c_str(); }
    char* str()  {  return &m_str[0]; }
    char operator[](size_t pos) const { return m_str[pos]; }
    char& operator[](size_t pos) { return m_str[pos]; }
    operator const char*() const { return str(); }  // cast operator
    operator char*() { return str(); }  // cast operator
protected:
    std::string m_str;
};

void main()
{
    String s = "1234";
    if(s[0] != '1')   //'String::operator []': 4 overloads have similar conversions !!!!!
        std::cout << "Error" << endl;
}

Проблема, конечно, теперь в том, что когда я пытаюсь использовать String [], я получаю сообщение об ошибке «4 перегрузки имеют похожие преобразования», компилятор не может решить, что из следующего делать:
1) использовать непосредственно оператор перегрузки []
2) сначала преобразовать в char * с помощью operator (), а затем operator [] над массивом char *

Есть ли способ сохранить обоих операторов? Для многих функций и т. Д., Принимающих char * и const char * для использования operstor () строки, но во всех других случаях напрямую operator [] (не пытаясь преобразовать в символ *).

Я полагаю, что ответ на это, вероятно, нет, и поэтому в std :: string нет оператора приведения char *, но, возможно, что-то изменилось в более позднем C ++ стандарты.

РЕДАКТИРОВАТЬ: я могу скомпилировать приведенное выше с помощью GCC, но это не удается с VS (x86). Ошибка C2666 'String :: operator []': 4 перегрузки имеют похожие преобразования.


person Baj Mile    schedule 21.11.2019    source источник
comment
... но не получается - хотите показать ошибки? Также включите предупреждения. Кроме того, большая часть вашего кода не нужна и только отвлекает, вам нужно извлечь и предоставить минимальный воспроизводимый пример на ваш вопрос. Включите этот встроенный, а не кучу разделов EDIT: ... О, кстати, строка заголовка не для вещей, для которых лучше использовать теги, удалите там C ++.   -  person Ulrich Eckhardt    schedule 21.11.2019
comment
Я указал ошибку в разделе кода - String :: operator [] ': 4 перегрузки имеют аналогичные преобразования   -  person Baj Mile    schedule 21.11.2019
comment
@ NathanOliver-ReinstateMonica Вопрос следует открыть повторно, ошибка воспроизводится в 32-битном режиме с clang-9 и mscv, см. gcc.godbolt.org/z/T4yTGH   -  person Maxim Egorushkin    schedule 22.11.2019


Ответы (1)


Всегда компилируйте код с включенными предупреждениями, для gcc и clang параметр командной строки - -Wall -Wextra -Werror. Предупреждения сообщат вам, что operator[] необходимо вернуть значение.


В выражении s[0] в 32-битном режиме компиляторы clang и msvc считают, что стандартное преобразование int индекса в unsigned (32-битный size_t) имеет тот же ранг, что и пользовательское преобразование от String до char*. См. Сообщения об ошибках компилятора здесь. Последнее преобразование, являющееся пользовательским преобразованием, имеет худший рейтинг, чем любое стандартное преобразование, но компиляторы не могут решить эту проблему. Я не уверен, какое именно стандартное правило здесь применяется.

Исправление состоит в том, чтобы добавить еще одну перегрузку operator[], которая принимает подписанный ptrdiff_t индекс:

char operator[](ptrdiff_t pos) const { return m_str[pos]; }
char& operator[](ptrdiff_t pos) { return m_str[pos]; }
person Maxim Egorushkin    schedule 21.11.2019
comment
Пожалуйста, попробуйте его с x86, для меня он строится только в VS2017 x64. - person Baj Mile; 21.11.2019
comment
Это более интересный вопрос, пока я удалю свой ответ. - person Maxim Egorushkin; 22.11.2019
comment
Спасибо, Максим. Теперь я вижу, что решение состоит в использовании int в operator [] - не могли бы вы объяснить немного больше, почему он работает с int, а не с size_t? Теперь я понимаю, что конфликт возникает только тогда, когда необходимо продвижение до unsigned int (size_t), и только для x86. - person Baj Mile; 22.11.2019