web-dev-qa-db-ja.com

戻り値の型をオーバーロードするテンプレートクラスの演算子

テンプレート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

そのためには、戻り値の型を変更する必要があります。

あなたのコードで:

// 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

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

TUがデフォルトで構築できない場合、std::declval<T>()genericity に存在します。型がintdoubleなどの組み込み型に限定されている場合は、_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