1

我有两个类,一个继承自另一个。基类的相关部分如下(显然这个类有ctors、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)。

但是,这会导致问题。当我将两个Polynomials 相加时,编译器使用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如果将两个Polynomials 传递给它,则返回 a )。如果可能的话,我想这样做,而不operator+知道每个可能的子类Vector(我认为这可能是一个合理的愿望?),并且不operator+为每个子类创建一个新函数(因为我还有其他几个重载的运算符,并且希望避免为每个派生类复制几乎完全相同的代码十次)。

我知道这在 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 >with 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 回答