Я пытаюсь создать шаблонный класс num. У этого класса должен быть общедоступный атрибут val с типом T, который является единственным параметром шаблона. Кроме того, если предоставляется значение, атрибут (val) должен быть инициализирован этим значением. Для этого я сделал следующий код:

#include <iostream>

template<class T>
class Num {
public: 
    T val;

    Num():val(0) { std::cout<<"default constr used"<<std::endl; }
    Num(T value):val(value) {std::cout<<"constr (T value) used"<<std::endl; }
    ~Num() { std::cout<<"destructor used"<<std::endl; }

    template<typename U>
    Num operator+(const Num<U>& other) {
        return val+other.value;
    }
};

Кроме того, я создал функцию main() для тестирования программы, которая выглядит так:

int main() {
    std::cout << Num<int>(1) + Num<double>(2.0);
    return 0;
}

Однако результат программы теперь 3. Тогда как я ожидал, что это будет 3.0 (типа double).

8
user12507648 10 Дек 2019 в 00:13
Если вы хотите удвоить, поскольку у вас есть код, написанный сейчас, вам нужно будет перевернуть эти операнды. Num<int>(1) + Num<double>(2.0); - это то же самое, что и Num<int>(1).operator+(Num<double>(2.0)), который вы объявили как возвращающий значение типа Num<int>.
 – 
scohe001
10 Дек 2019 в 00:16
1
Это даже не будет компилироваться как есть.
 – 
Ted Lyngmo
10 Дек 2019 в 00:16
Почему это приведет к ошибке компиляции?
 – 
Juan Carlos Ramirez
10 Дек 2019 в 00:43
3
Поскольку в классе нет переменной-члена с именем value.
 – 
Ted Lyngmo
10 Дек 2019 в 00:50
Ты прав, этого не видел.
 – 
Juan Carlos Ramirez
10 Дек 2019 в 00:52

2 ответа

Для этого вам нужно будет изменить тип возвращаемого значения.

В вашем коде:

// vvv---- Means Num<T>
   Num operator+(const Num<U>& other) {
       return val + other.val;
   }

Действительно, внутри шаблона класса вы можете ввести имя класса без аргументов шаблона, и это будет в некоторой степени эквивалентно написанию Num<T>.

Ваша функция всегда возвращает тип первого операнта, независимо от типа самого добавления.

Вы хотите вывести этот тип из сложения:

auto operator+(const Num<U>& other) -> Num<decltype(val + other.val)> {
    return val + other.val;
}

Таким образом, это всегда правильный тип возвращаемого значения в соответствии с правилами оператора C ++.

10
Guillaume Racicot 16 Дек 2019 в 20:52
2
В C ++ 14 и более поздних версиях не требуется -> Num<decltype(val + other.val)>
 – 
BЈовић
10 Дек 2019 в 12:17
@ BЈовић, ему это нужно. В противном случае возвращаемый тип будет decltype(val + other.val), а не Num<decltype(val + other.val)>.
 – 
Evg
10 Дек 2019 в 16:50
В C ++ 17 я бы использовал вывод типа возвращаемого значения и вывод аргумента шаблона класса, чтобы вернуть Num{val + other.val}
 – 
Guillaume Racicot
10 Дек 2019 в 17:27

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

Например (с использованием вывода типа возвращаемого значения C ++ 14):

template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
    using R = decltype(std::declval<T>() + std::declval<U>());
    return Num<R>{x.val + y.val};
}

std::declval<T>() предназначен для универсальности, если T и / или U не являются конструктивными по умолчанию. Если типы ограничены встроенными, например int и double, его можно заменить на T{} или T():

using R = decltype(T{} + U{});

С помощью вывода аргументов шаблона класса в C ++ 17 это можно еще больше упростить:

template<class T, class U>
auto operator+(const Num<T>& x, const Num<U>& y) {
    return Num{x.val + y.val};
}
9
Evg 10 Дек 2019 в 00:34
Вау, я понятия не имел, что вы можете auto удалить возвращаемый тип. Это довольно круто.
 – 
scohe001
10 Дек 2019 в 00:22
@ scohe001, это функция C ++ 14.
 – 
Evg
10 Дек 2019 в 00:23