Разница в C ++ между автоматическим преобразованием типов в std :: string и char *
-
22-07-2019 - |
Вопрос
В качестве учебного упражнения я рассмотрел, как работает автоматическое преобразование типов в 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 более интригующе ...