Непонятное использование оператора double()

У меня есть класс Rectangle с операторами преобразования как в double, так и в std::string:

class Rectangle
{
public:
    Rectangle(double x, double y) : _x(x), _y(y) {}
    operator std::string ();
    operator double ();
private:
    double _x, _y;
    double getArea() {return _x * _y;}
};

int main()
{
    Rectangle r(3, 2.5);
    cout << r << endl;
    return 0;
}

Я не понимаю, почему вызывается operator double(), а не operator std::string(). Насколько мне известно, согласно викибуку по C++, operator double является используется для преобразования Rectangle объектов в double.

Так что же здесь происходит? Связано ли это с тем, что конструктору передается int? Если да, то почему?


person Alex Goft    schedule 16.09.2015    source источник
comment
Что вообще означает преобразование прямоугольника в двойное.   -  person    schedule 16.09.2015
comment
я думаю (двойной) г или что-то   -  person Alex Goft    schedule 16.09.2015
comment
@AlexGoft Нет, другой Алекс имел в виду «что это значит логически?». Со стороны операция кажется бессмысленной.   -  person Konrad Rudolph    schedule 16.09.2015
comment
Я подозреваю, что он имел в виду область, поскольку нет другой причины включать функцию getArea() в образец.   -  person Zac Crites    schedule 16.09.2015


Ответы (3)


У вас нет оператора для вывода прямоугольника в поток. cout имеет перегрузку, которая принимает double, и ваш класс может быть неявно преобразован в double, поэтому он выбран.

Причина, по которой перегрузка строки не выбрана и не рассматривается как двусмысленность, заключается в том, что operator << для строки является функцией-членом и не включена в перегрузка члена и перегрузка, не являющаяся членом набор из cout. Если мы закомментируем operator double, мы увидим, что мы получаем ошибку компиляции.

Если мы хотим вызвать operator string, нам нужно будет явно привести r к строке. Живой пример

person NathanOliver    schedule 16.09.2015
comment
Тогда почему он не выбирает конверсию string? - person interjay; 16.09.2015
comment
@interjay Я просто изучал это. Я не видел этого оператора, когда писал ответ, - person NathanOliver; 16.09.2015
comment
Хорошо поймал. Если бы кто-то захотел объяснить, почему реализация operator double() для типов, не представляющих числа, является плохой практикой, ему было бы трудно найти лучшую иллюстрацию :-) - person Sergey Kalinichenko; 16.09.2015
comment
std::ostream::operator<<(double) предпочтительнее std::basic_string::operator(basic_ostream&, const basic_string&) - person Simon Kraemer; 16.09.2015
comment
@SimonKraemer Дело не в том, что одно предпочтительнее другого, а в том, что другое просто никогда не рассматривается. - person Oktalist; 16.09.2015

Поскольку вы не предоставили перегрузку operator<< для Rectangle, компилятор рассматривает другие перегрузки, для которых аргументы могут быть преобразованы в типы параметров.

Если какая-либо из перегрузок является шаблоном, то подстановка аргумента шаблона происходит с ними до разрешения перегрузки. Компилятор пытается вывести параметры шаблона из типов аргументов, переданных функции.

Перегрузка string не рассматривается из-за ошибки подстановки аргумента шаблона:

template <class CharT, class Traits, class Allocator>
std::basic_ostream<CharT, Traits>&
    operator<<(std::basic_ostream<CharT, Traits>& os,
               const std::basic_string<CharT, Traits, Allocator>& str);

Подстановка аргументов шаблона не учитывает пользовательские преобразования, поэтому компилятор не может вывести типы CharT, Traits или Allocator из типа Rectangle, поэтому эта перегрузка не участвует в разрешении перегрузки. (Напомним, что std::string — это просто определение типа std::basic_string<char, std::char_traits<char>, std::allocator<char>>.)

Следовательно, есть одна перегрузка operator<<, которая лучше подходит, чем любая другая, и это перегрузка double. Не шаблон, а функция-член шаблона класса.

basic_ostream<CharT, Traits>& basic_ostream<CharT, Traits>::operator<<(double);
person Oktalist    schedule 16.09.2015
comment
Обратите внимание, что последний шаблон тривиален; std::cout наследуется от std::basic_ostream<char, std::char_traits<char>>. Нет необходимости в подстановке аргумента шаблона. - person MSalters; 16.09.2015

В двойной перегрузке нет ничего особенного, кроме перегрузок других примитивных типов. В данном случае это единственная доступная примитивная перегрузка. Компилятор будет вести себя одинаково для int, char и т. д.

Обратите внимание, что если у нас будет более одной перегрузки примитивного типа, компилятор выдаст

error: ambiguous overload for 'operator<<' ...
person AmirArbel    schedule 16.01.2019