Tengo una clase de plantilla que se basa en un std::vector. Parece que...

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

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

Quiero crear una clase de plantilla derivada, ComplexVector que requerirá que el tipo de datos sea std::complex<T>, y, por supuesto, quiero reutilizar los métodos, las sobrecargas del operador, etc., que creé en la clase { {X2}}.

El problema es que parece que hay dos formas de hacerlo.

1) Fuerce la declaración de std::complex<T> como tipo de plantilla.

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

2) Declare ComplexVector solo con el tipo escalar, pero luego pase el tipo std::complex al constructor Vector.

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

La opción 1 es mucho más simple en términos de desarrollo del software, pero es fea para el usuario, mientras que la opción 2 es mucho más agradable para el usuario. Me gustaría hacer la opción # 2, pero me preocupa cómo funcionará.

Si paso std::vector<T> al constructor de la clase base, ¿eso solo cambiará lo que hace el constructor, o la plantilla completa pasará del tipo T a std::complex< T >?

Si no todo se convierte a std::complex<T>, ¿eso significa que tendría que sobrecargar todos los métodos que se encuentran en Vector?

2
Jim Clay 8 sep. 2018 a las 03:02

3 respuestas

La mejor respuesta

Tipo de plantilla

Si realmente no desea tener especialización, simplemente puede usar un tipo de definición con plantilla de esta manera:

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

Los usuarios podrían usar ComplexVector<float> y representaría correctamente un Vector<std::complex<T>>.
Esta es una solución muy simple que no se ajusta a tus necesidades. Eche un vistazo a las siguientes soluciones para su problema específico.

Herencia de plantilla

Si su objetivo es cambiar solo métodos específicos cuando T es std::complex<U>, debe heredar de Vector así:

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

Esto también le permite agregar métodos solo disponibles para vectores basados en complex.

Agregar un método solo para ComplexVector

Si desea poder "mejorar" ComplexVector, puede hacerlo así:

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

Demo de trabajo aquí

Usar un rasgo de tipo pequeño para verificar el tipo

Usando algunas plantillas, podemos crear una pequeña ayuda para determinar si un tipo dado es un ComplexVector o no. Esto podría ser útil si desea llamar a los métodos específicos de forma segura en un entorno pesado de plantilla:

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

Aquí hay otra pequeña demostración para demostrar este comportamiento en la práctica
value se determinará en la compilación. Esto podría permitirle usar algunos SFINAE para controlar mejor el flujo de su programa (O puede usar el constexpr if si está usando C ++ 17.)

9
Telokis 10 sep. 2018 a las 16:00

Creo que estás confundido al usar el nombre T dos veces. Mira esto de esta manera:

template<typename T>
class Vector
{ ... }

template<typename U>
class ComplexVector : public Vector<std::complex<U>>
{ ... }

ComplexVector<float>; // U = float, T = std::complex<float>
5
Dani 10 sep. 2018 a las 15:22

ComplexVector<std::complex<T> > es redundante (ya dijiste que era un vector complejo, ¿verdad?).

Usando la convención general llegarás a:

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

Agregar la especialización de Vector (como algunos sugirieron), mientras que también la herencia no es directamente posible y se necesita un (quizás difícil) refactor que involucra Vector<T>, pero básicamente implica mover la mayoría / todas las partes a una nueva clase base común. Puede ser una buena idea si ya está trabajando en Vector<T>, por ejemplo.

0
darune 12 sep. 2018 a las 19:16