Разница в C ++ между автоматическим преобразованием типов в std :: string и char *

StackOverflow https://stackoverflow.com/questions/1443870

Вопрос

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

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

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

С другой стороны, если я немного изменю его на класс char, произойдет автоматическое преобразование , хотя сравнение 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 на самом деле является экземпляром шаблона класса 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;   
}

Другие советы

В первом примере два сравниваемых класса (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 есть оператор ==, все работает.

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top