基础/派生模板类类型

时间:2018-09-08 00:02:31

标签: c++ c++11 templates inheritance

我有一个围绕std::vector构建的模板类。看起来...

template <class T>
class Vector {
public:
    std::vector<T> vec;

    // Constructors...
    // Operator overloads...
    // Methods...
    // etc.
};

我想创建一个派生模板类ComplexVector,它将要求数据类型为std::complex<T>,当然,我想重用我创建的方法,运算符重载等。在Vector类中。

问题在于,似乎有两种方法可以做到这一点。

1)强制声明std::complex<T>作为模板类型。

ComplexVector< std::complex<float> > myVec;

2)仅使用标量类型声明ComplexVector,然后将std::complex类型传递给Vector构造函数。

ComplexVector<float> myVec;
// Constructor
ComplexVector<T>::ComplexVector() : Vector< std::complex<T> >

选项1在开发软件方面要简单得多,但对用户而言却很丑陋,而选项2对用户而言则更好。我想做第二种选择,但我担心它会如何工作。

如果我将std::vector<T>传递给基类的构造函数,这是否只会改变构造函数的作用,还是整个模板将从类型T变为std::complex< T >

如果不能全部转换为std::complex<T>,这是否意味着我将不得不重载Vector中的所有方法?

4 个答案:

答案 0 :(得分:9)

模板化typedef

如果您真的不想专门化,则可以像这样使用模板化typedef:

template <typename T>
using ComplexVector = Vector<std::complex<T>>;

然后用户可以使用ComplexVector<float>,它将正确表示一个Vector<std::complex<T>>
这是一个非常简单的解决方案,无法完全满足您的需求。查看针对您的特定问题的以下解决方案。

模板化继承

如果您的目标是仅在Tstd::complex<U>时更改特定方法,则您必须像这样从Vector继承:

template <typename T>
class Vector
{
    public:
    bool is_complex() { return false; }
};

template <typename U>
class ComplexVector : Vector<std::complex<U>>
{
  public:
    bool is_complex() { return true; }
};

这还允许您添加仅适用于基于complex的向量的方法。

仅为ComplexVector添加方法

如果您想“增强” ComplexVector,可以这样做:

#include <iostream>
#include <complex>

template <typename T>
class Vector
{
  public:
    bool is_complex() { return false; }
};

template <typename U>
class ComplexVector : Vector<std::complex<U>>
{
  public:
    bool is_complex() { return true; }
    bool only_for_complex() { return true; } // Only in ComplexVector
};

int main() {
  Vector<float> float_vec;
  ComplexVector<float> complex_vec;

  std::cout << "Is float_vec complex? " << float_vec.is_complex() << "\n";
  std::cout << "Is complex_vec complex? " << complex_vec.is_complex() << "\n";

  // The following line doesn't compile
  // std::cout << "Is only_for_complex method in float_vec? " << float_vec.only_for_complex() << "\n";
  std::cout << "Is only_for_complex method in complex_vec? " << complex_vec.only_for_complex() << "\n";

  return 0;
}

Working demo here

使用小型字体特征检查类型

使用一些模板,我们可以创建一个小助手来确定给定类型是否为ComplexVector。如果想在模板繁重的环境中安全地调用特定方法,这可能会派上用场:

// For any type T, value is false.
template <typename T>
struct is_complex_vector
{
    static const bool value = false;
};

// We specialize the struct so that, for any type U,
// passing ComplexVector<U> makes value true
template <>
template <typename U>
struct is_complex_vector<ComplexVector<U>>
{
    static const bool value = true;
};

is_complex_vector<typeof(float_vec)>::value; // is false
is_complex_vector<typeof(complex_vec)>::value; // is true

Here is another small demo to demonstrate this behavior in practice
value将在编译时确定。这可以允许您使用一些SFINAE技巧,以便更好地控制程序的流程。 (或者,如果您使用的是constexpr if,则可以使用C++17。)

答案 1 :(得分:5)

我认为您两次使用名称T感到困惑。这样看:

SERVICE_CONTROL_SESSIONCHANGE

答案 2 :(得分:2)

在原始q。的comments中,@ JimClay阐明了用例:他想使用fft(快速傅立叶变换)之类的方法扩展复杂矢量。

当然可以从vector<T>派生一个新类,例如ComplexVector<T>并添加一个类似fft的方法,但是我不建议这样做。详细原因如下。更好的选择是添加一个独立功能,例如

template<typename T>
vector<complex<T>> fft(const vector<complex<T>>&);

(为了方便阅读,该标签与using namespace std一起使用。

以下是不使用派生模板类的原因

  • 假设您要使用一个返回vector<complex<T>> v的库函数。如果您滚动了自己的ComplexVector<T>,则需要使用v并将其转换为ComplexVector<T>,然后再调用fft。可以有效地完成 ,但是它要求用户在移动构造时要小心。
  • 情况变得更糟。假设您和我处于相同的情况,并且都滚动了我们自己的类MyComplexVector<T>YourComplexVector<T>。然后,想要一起使用我们的库的用户将需要使用MyComplexVector<T>,将其转换为vector<T>,然后再转换为YourComplexVector<T>,然后再调用我的库。 [尽管在大多数情况下,第一次转换会隐式发生。]
  • 用户需要做一些工作来熟悉ComplexVector<T>的语义,而每个C ++程序员都知道vector<T>的行为。

我已经大到可以在STL之前使用C ++了,并且一起使用库并不好玩!确实,库互操作性是引入标准stringvector等的主要原因之一。

请注意,如果您想实现高效的就地fft,则需要一个右值引用版本:

template<typename T>
vector<complex<T>> fft(vector<complex<T>>&&);

答案 3 :(得分:0)

ComplexVector<std::complex<T> >是多余的(您已经说过它是复数向量吧?)。

使用一般约定,您将到达:

class ComplexVector< T > : public Vector< std::complex<T> > {
public:
  typedef std::complex<T> type;//for convenience, etc.
  //...
}

添加Vector的特殊化(如某些建议),同时也不能直接进行继承,并且需要Vector<T>-的重构(可能很困难),但是从根本上讲,它涉及将大部分/所有部分移动到新的通用基础上类。例如,如果您已经在Vector<T>上工作,那可能是个好主意。