Двоичный оператор для класса шаблона не разрешает неявное преобразование - PullRequest
1 голос
/ 22 мая 2019

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

template<typename T>
struct foo
{
    foo(T val) : val{ val } {}
    T val;
};

struct bar
{
    bar(double val) : val{ val } {}
    double val;
};

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

template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
    return foo<T>(a.val + b.val);
}

bar operator+(bar a, bar b)
{
    return bar(a.val + b.val);
}

Когда яиспользуйте эти операторы с неявным преобразованием, оператор, использующий объект типа foo, не использует неявное преобразование для двойного значения, чтобы применить мой перегруженный оператор, даже если он может сделать это для не шаблонного класса.В результате отсутствует оператор, соответствующий типам в этом выражении.

int main()
{
    foo<double> foo_value(11.0);
    bar bar_value(11.0);

    foo<double> ev1 = foo_value + 1.0; // no operator matches these operands
    bar ev2 = bar_value + 1.0;
}

Должен ли оператор сначала быть явно создан?Если это так, а) как это выглядит, и б) почему инстанцирование не выполняется неявно, если это можно сделать при инициализации объекта типа foo<double>?

Если стандарт неПоддерживать любое разрешение без явного приведения 1.0 к значению типа foo<double>. Полагаю, единственной другой возможностью является определение перегрузок операторов для каждого типа, который я хочу использовать подобным образом (как для lhs, так и для rhs)?

1 Ответ

2 голосов
/ 22 мая 2019

Что нужно помнить о шаблонах, так это то, что они не сделают для вас преобразование. Все, что они делают, это пытаются выяснить типы вещей, и если это сопряжено с параметрами шаблона, то оно вытеснит функцию и вызовет ее.

В вашем случае, когда вы делаете

foo_value + 1.0

компилятор работает нормально, давайте посмотрим, есть ли у нас operator +, который будет работать для этого. Находит

template<typename T>
foo<T> operator+(foo<T> a, foo<T> b)
{
    return foo<T>(a.val + b.val);
}

и затем он пытается выяснить, что такое T, чтобы он мог выделить конкретную функцию. Он смотрит на foo_value, видит, что это foo<double>, поэтому он говорит, что для первого параметра T должен быть double. Затем он смотрит на 1.0 и все в порядке, у меня есть double, и именно тогда вы сталкиваетесь с проблемой. Компилятор не может определить, что T должно быть для b, потому что он ожидает foo<some_type>, но вместо этого получил double. Поскольку он не может определить тип, ваш код не может быть скомпилирован.

Чтобы получить желаемое поведение, вам нужно добавить

template<typename T>
foo<T> operator+(foo<T> a, T b)
{
    return foo<T>(a.val + b);
}

Что позволяет добавить T к foo<T>, или еще лучше

template<typename T, typename U>
foo<T> operator+(foo<T> a, U b)
{
    return foo<T>(a.val + b);
}

Что позволяет вам добавить что-либо в foo<T> (foo<double> + int, например, если первая версия этого не допустит)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...