Разница в C ++ между автоматическим преобразованием типов в std :: string и char * - PullRequest
4 голосов
/ 18 сентября 2009

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

Я создал класс StdStringConverter, который можно автоматически преобразовать в std::string, но компилятор (g ++ 4.3.4 в Debian), по-видимому, не выполняет преобразование при сравнении объекта с реальным std::string (пожалуйста, игнорируйте отсутствие передачи по ссылке и ненужное создание временных объектов):

#include <string>

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

С другой стороны, если я немного изменю его на класс CStringConverter, произойдет автоматическое преобразование , хотя сравнение указателей char, вероятно, не то, что я намеревался:

#include <string>

class CStringConverter
{
public:
    explicit CStringConverter(std::string name) : m_name(name) {}
    operator const char* () const { return m_name.c_str(); }
private:
    std::string m_name;
};

int main()
{
    CStringConverter converter(std::string("Me"));
    const char* name = "Me";
    // Next line compiles fine, but they are not equal because the
    // pointers don't match.
    return (converter == name) ? 0 : 1;
}

Есть ли что-то особенное в разнице между std::string и char* в этом контексте, что компилятор не обрабатывает их одинаково?

Ответы [ 3 ]

7 голосов
/ 18 сентября 2009

Проблема связана с тем, что std :: string на самом деле является экземпляром шаблона класса std :: basic_string. Оператор ==, доступный в пространстве имен std, принимает два шаблона std :: basic_string:


template<class charT, class traits, class Allocator>
bool operator==(const basic_string& lhs,
                const basic_string& rhs);

Если бы эта версия оператора == была перегружена специально для std :: string, ваш код был бы в порядке. Но это не тот случай, когда компилятору потребуется выводить аргументы шаблона для параметров шаблона std :: basic_string, чтобы он мог понять, что возвращение вашего оператора преобразования является возможным совпадением.

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

Я могу предложить вам разместить StdStringConverter в пространстве имен и предоставить версию оператора == для std :: string в этом пространстве имен. Таким образом, когда ваш компилятор находит такое выражение, ADL (Argument Dependent Lookup) вступает в игру, и все работает нормально.


#include <string>

namespace n1 {

class StdStringConverter
{
public:
    explicit StdStringConverter(std::string name) : m_name(name) {}
    operator std::string () { return m_name; }
private:
    std::string m_name;
};

bool operator==(std::string const& a, std::string const& b)
{
  return a == b; //EDIT: See Paul's comment on std::operator== here.
}

}

int main()
{
    using namespace n1;
    StdStringConverter converter(std::string("Me"));
    std::string name = "Me";
    return (converter == name) ? 0 : 1;   
}
1 голос
/ 18 сентября 2009

В первом примере два сравниваемых класса (string и StdStringConverter) не получают какой-либо специальной обработки от компилятора для преобразования типов. Это означает, что перегрузка оператора, которую вы произвели, даже не срабатывает. Компилятор просматривает список операторов == перегрузок, и ни один из них не принимает StdStringConverter, поэтому он кричит на вас.

Во втором примере имя char *. Поскольку это примитивный тип, то компилятор пытается преобразовать не примитив в тип char *. Поскольку у вас есть переопределение, оно работает, и вы сравниваете адреса.

Компилятор не будет явным приведением типов к операциям, которые не включают примитивные типы. Что-то, что он сделает, попытается использовать конструкторы, чтобы типы соответствовали. Например, если вы измените свой первый пример на это:

#include <string>

class StdStringConverter
{
public:
    StdStringConverter(std::string name) : m_name(name) {}
    bool operator==(const StdStringConverter &name) { return m_name == name.m_name; }
    operator const std::string () const { return m_name; }
private:
    std::string m_name;
};

int main()
{
    StdStringConverter converter(std::string("Me"));
    const std::string name = "Me";
    // Next line causes compiler error:
    // no match for 'operator==' in 'converter == name'
    return (converter == name) ? 0 : 1;
}

Теперь программа возвращает 0. Поскольку конструктор теперь не является явным, компилятор попытается использовать его для преобразования строки в StdStringConverter. Поскольку теперь в StdStringConverter есть оператор ==, все работает.

0 голосов
/ 18 сентября 2009

Есть несколько факторов. Если вы измените оператор возврата таким образом

return (std :: operator == (имя, имя))? 0: 1;

он компилируется, хотя, очевидно, не делает то же самое. С другой стороны

return (std :: operator == (конвертер, имя))? 0: 1;

не дает, но выдает более интересное сообщение об ошибке

нет соответствующей функции для вызова оператора = == (StdStringConverter &, const std :: string &)

, который напоминает мне, что operator == создан на основе basic_string <>, который имеет три параметра шаблона для загрузки. Если вы используете int в вашем примере, а не std :: string, компилятор не будет жаловаться.

Как получить желаемый эффект с помощью std :: string более интригующе ...

...