So basically, I'm working on a project that can require both arrays and vectors to be passed through in order to calculate the final outcome. Now, I'm using the constructor to calculate the values and I need to know whether or not it is possible to change the datatype of the values depending of that what is passed through the constructor. Example:

class FFT {

    public:

        FFT(<array> &values_to_be_passed_through || <vector> &values_to_be_passed_through)
        {
            this->val = foo;
        }


    private:
        vector/array vals_to_be_passed_through

};


int main(int argc, char *argv[]) {

    int arr[] = {1, 2, 3, 4, 5, ......., 10};

    FFT fft(arr); // this would work.

    vector<int> values = {1, 2, 3, 4, 5, ......., 10};

    FFT fft(values); // this would also work.   
}

I know what the return type of the function would always be. i.e. in this case, it would be a complex (I've already wrote this) but I need to alternate to allow for multiple datatypes to be passed through.

Would this be possible using templates?

I hope someone can help me :)

Edited 3 Years Ago by phorce

Just use a vector internally and copy the array into it as an alternative constructor. Or you could have the underlying collection type be a template argument to the class. Or you could use inheritance from an interface to define both an array-based class and a vector-based class.

But I'd favor using a vector and taking an array initializer in one of the constructors for sheer simplicity of design.

Edited 3 Years Ago by deceptikon

Do you really need a class?

Computing the FFT seems more appropriate as a kind of function than a kind of class. And you can make the choice of input / output containers external to that function by using the established C++ practice of using generic iterators. For example, you can model your function after the std::transform or std::copy functions. Simply do this:

template <typename InputIter, typename OutputIter>
OutputIter compute_fft(InputIter first, InputIter last, OutputIter dest) {
  // do the fft transformation here.
  // store individual results in "dest", e.g., *(dest++) = <next_value>;
  return dest;
};

int main() {

  int arr[] = {1, 2, 3, 4, 5, ......., 10};
  vector<int> result;
  compute_fft(arr, arr + sizeof(arr) / sizeof(int), back_inserter(result));

  vector<int> values = {1, 2, 3, 4, 5, ......., 10};
  vector<int> result_values( values.size() );
  compute_fft(values.begin(), values.end(), result_values.begin());

};

Just to add to that, two things I forgot to mention.

If you need to have "parameters" to dictate how you do the FFT (or whatever else), you can simply have a class with a template member funtion:

class FFT {
  private:

    /* some configuration parameters as data members */

  public:

    FFT( /* some params */ );

    template <typename InputIter, typename OutputIter>
    OutputIter transform_range(InputIter first, InputIter last, OutputIter out) const {
      /* do the FFT, using the params */
      return out;
    };

};

Also, if you still want to have a class that contains the result of the FFT, like you have in the original post, then you can simply use the generic function template that computes the FFT as the implementation of the FFT that your class uses. This means you get a "simple" but not-so-generic class for people to use and a zero-overhead generic function under-the-hood. In any given situation, you can use whichever is more appropriate: the generic function template for the more "special" situations; and, the wrapper class for usual (or less performance-critical) situations. I use this kind of pattern all the time, it is very convenient.

A class that has a constructor that accepts a range:

#include <iostream>
#include <initializer_list>
#include <iterator>
#include <array>
#include <vector>
#include <sstream>

struct A
{
    template < typename ITERATOR > A( ITERATOR begin, ITERATOR end )
    {
        std::cout << "A::constructor - " ;
        for( ; begin != end ; ++begin ) std::cout << *begin << ' ' ;
        std::cout << '\n' ;
    }

    template < typename T >
    A( std::initializer_list<T> range ) : A( std::begin(range), std::end(range) ) {}

    template < typename RANGE >
    A( const RANGE& range, decltype( std::begin(range) )* = nullptr )
                          : A( std::begin(range), std::end(range) ) {}
};

int main()
{
    double carray[] { 10.1, 20.2, 30.3, 40.4 } ;
    A a1(carray) ; // construct from C-array (range)

    std::array<long,5> array { { 12, 13, 14 } } ;
    A a2(array) ; // from std::array (range)

    std::vector<short> vector { 91, 92, 93 } ;
    A a3(vector) ; // from sequence container (range)

    A a4( { 1, 2, 3, 4, 5 } ) ; // from initializer list

    std::istringstream stm( "50 60 70 80" ) ;
    // from pair of iterators
    A a5( (std::istream_iterator<int>(stm)), std::istream_iterator<int>() ) ;
}

http://ideone.com/O5wfAv

This article has been dead for over six months. Start a new discussion instead.