1

2 つのクラスがあり、一方は他方から継承されます。基本クラスの関連部分は次のとおりです(明らかに、このクラスにはctor、dtorなど、特に がありますが、目前operator[]の問題とは無関係だと思いました):

#include <array>

template < class T, unsigned int N >
class Vector
{
public:
    template < class U, unsigned int M > friend Vector< U, M > operator+ ( const Vector< U, M >&, const Vector< U, M >& );

    template < class U, unsigned int M > friend std::ostream& operator<< ( std::ostream&, const Vector< U, M >& );
};

派生クラス (繰り返しになりますが、無関係だと思われる部分を削除したことは明らかです):

#include "Vector.h"

template < class T, unsigned int N >
class Polynomial
    : public Vector< T, N >
{
public:
    template < class U, unsigned int M > friend std::ostream& operator<< ( std::ostream&, const Polynomial< U, M >& );
};

(注: フレンド関数は、テンプレートにクラスとは異なる文字を使用します。そうしないと、gcc が「シャドウイング」について文句を言うためです。ただし、ロジックは同じです。)

Vectors 一方向に出力します (例: < 3, 5, 1 >)。Polynomials 別のものを出力します (例: 3 x^2 + 5 x + 1)。

ただし、これは問題を引き起こします。2 つの を一緒に追加するPolynomialと、コンパイラは を使用します。template < class U, unsigned int M > Vector< U, M > operator+ ( const Vector< U, M >&, const Vector< U, M >& )これはもちろん を返しますVector。したがって、のようなことをしようとするとstd::cout << poly1 + poly2;、結果の表示は間違った形式になります。

template < class U, unsigned int M > Vector< U, M > operator+ ( const Vector< U, M >&, const Vector< U, M >& )パラメータの実際のデータ型を検出し、それに応じて戻り値をキャストするように変更したいと思います (たとえばPolynomial、2 つPolynomialの s が渡された場合は a を返します)。可能であれば、可能なoperator+すべてのサブクラスを知らずにVector(これはおそらく正当な欲求だと思いますか?)、operator+サブクラスごとに新しい関数を作成せずに、これを実行したいと思います(他にもいくつかのオーバーロードされた演算子があるため、派生クラスごとにほぼ同じコードを 10 回コピーすることは避けたいと考えています)。

Python ではこれが可能である (そして、実際には比較的簡単である) ことを私は知っています。C++ はそのようなことをサポートしていますか?

4

1 に答える 1

2

結果を として計算する場合、Vector<T,N>単純に (合法的に) にキャストすることはできませんPolynomial<T,N>。目的の効果を得るには、いくつかのより深い変更が必要です。目的の結果タイプを提供できる無料operator+の実装と、から派生したすべてを検出する方法が必要Vector<T,N>です。構築しましょう。

a) すべて検出Vector<T,N>

そのために、空の基本最適化 (EBO) によって最適化され、次の方法で検出できる空の基本クラスから派生させることができますstd::enable_if

struct VectorBase {};

template< class T, unsigned int N >
class Vector
{
  // ...
};

Uから派生したクラスであるかどうVector< T, N >かを確認できるようになりましたstd::is_base_of< VectorBase, U >::value。完全に正しいためには、VectorBaseそれ自体を除外する必要があります ( !std::is_same< U, VectorBase >::value) が、それはおそらくユース ケースには必要ありません。

b) 目的の戻り値の型を提供する実装。その前に:

template< class T, unsigned int N >
class Vector
{
    template < class U, unsigned int M >
    friend Vector< U, M > operator+ ( const Vector< U, M >&, const Vector< U, M >& );
};

次のものに置き換える必要があります。

template< class T, unsigned int N >
class Vector
{
    friend Vector< T, N > operator+ ( const Vector< T, N >&, const Vector< T, N >& );
};

一般的な場合。ただし、後で になる特別な戻り値の型が必要なPolynomial<T,N>ので、次のようにします。

template< class T, unsigned int N >
class Vector
{
public:
    template< typename R >
    static R add( const Vector< T, N >& lhs, const Vector< T, N >& rhs )
    {
        static_assert( std::is_base_of<VectorBase,R>::value,
                       "R needs to be derived from Vector<T,N>" );
        R result;
        // implement it here...
        return result;
    }
};

c)operator+を呼び出しadd、SFINAE によって保護されている を提供します。

// as a free function:
template< typename V >
typename std::enable_if< std::is_base_of< VectorBase, V >::value, V >::type
operator+( const V& lhs, const V& rhs )
{
  return V::template add<V>( lhs, rhs );
}

いくつかの小さなタイプミスを除いて (私はテストしていません)、この戦略はうまくいくはずです。

于 2013-03-27T18:08:25.543 に答える