user2138149
user2138149

Reputation: 16705

C++ How do I cast from 1 template class to another?

I have a class which I intend to use with either the type float or double. As far as I am aware, there is no way to restrict template options, so perhaps I might be doing something dangerous here?

template<class T>
class A
{
    A(T arg) { _data = arg; }

    T _data;
}

typedef A<float> A_f;
typedef A<double> A_d;

How can I do the following?

int main()
{
    A_f f(3.1415);
    A_d d(3.1415);
    f = (A_f)d;
}

IE: Cast the class containing data of type double to the class containing data of type float.

Edit: This doesn't seem to be going anywhere, so I tried playing around with this, but obviously I have no idea what to do here so it doesn't compile...

template<class T>
class A
{
    friend                    // Intention is for T to be double here
    A<float> operator A<float>(const A<T> input);
}

A<float> operator A<float>(const A<double> input)
{
    return A<float>(input._data);
}

Maybe this helps explain what I want to achieve?

Second Edit for Adam:

return A<float>((float)input._data);

Is this better?

Upvotes: 1

Views: 7062

Answers (5)

marcinj
marcinj

Reputation: 49986

Example with template conversion operator + static_assert for type verify:

http://coliru.stacked-crooked.com/a/6b01010ea5f02aee

#include <vector>
#include <iostream>

template < typename T > class TD; // type visualiser

template<class T>
class A
{
    public:
    A(T arg) { _data = arg; }

    template<typename D>
    operator A<D>() {
      static_assert(std::is_same<D, float>::value || std::is_same<D, double>::value, "double/floats allowed only");
      //TD<D>();  // D is float here
      return static_cast<D>(_data);
    }

    T _data;
};

typedef A<float> A_f;
typedef A<double> A_d;
typedef A<int> A_i;

int main() {
   A_f f(3.14151);
    A_d d(3.14152);

    std::cout << f._data << std::endl;
    std::cout << d._data << std::endl;    

    f = (A_f)d;
    //f = (A_i)d; // static assertion here

    std::cout << f._data << std::endl;
    return 0;
}

[edit]

template<class T>
class A
{
public:
   A(T arg) { _data = arg; }

  template<typename D>
  operator A<D>() {
    static_assert(std::is_same<D, float>::value || std::is_same<D, double>::value, "double/floats allowed only");
    //TD<D>();  // D is float here
    return A<D>(static_cast<D>(_data));
  }

  T _data;
};

Upvotes: 0

Adam
Adam

Reputation: 17329

I'll second the arguments that you should not cast like that, but if you insist, add a templated copy constructor:

template<class T>
class A
{
public:                                 // add this
    A(T arg) { _data = arg; }

    template <class U>                  // add this
    A(A<U> arg) { _data = arg._data; }  // add this

    T _data;
}

This will then allow conversions from A<U> to A<T> as long as U is implicitly convertible to T.

Upvotes: 1

user4386938
user4386938

Reputation:

There is an option to customize a class based on the type you provide it. The technique is called 'template specialization'

#include <iostream>
using namespace std;

template <typename T>
class A {
public:
    void print_my_type() { 
        cout << "Generic template instance" << endl; 
    }

    explicit operator A<int>() const {
        cout << "Casting to int" << endl;
        return A<int>();
    }
};

template <>
class A<int> {
public:
    void print_my_type() { 
        cout << "Class templated with an int" << endl; 
    }

    explicit operator A<double>() const {
        cout << "Casting to double" << endl;
        return A<double>();
    }
};

int main() {
    A<double> a;
    A<int> b;

    a.print_my_type();
    b.print_my_type();

    a = static_cast<A<double>>(b);

    return 0;
}

You should not cast objects like that. If you intend to have an object that you want to cast to another. You should provide it an operator A() method so it can handle conversions gracefully

Upvotes: 0

tux3
tux3

Reputation: 7320

You could use std::enable_if to only allow certain types :

#include <type_traits>
using namespace std;

// Our catch-all is not defined, so will not compile
// Could also be made to print a nice error message
template<typename T, typename Sfinae = void> class A;

// Ok if T is float or double
template<typename T>
class A<T, typename std::enable_if<std::is_same<T, float>::value
                                || std::is_same<T, double>::value>::type>
{
    // Your class here
};

int main()
{
    A<int> a; // FAILS
    A<float> b; // Ok
    A<double> c; // Ok

    return 0;
}

Then you just need to define a conversion operator in your class for the cast to work.

Upvotes: 3

user2249683
user2249683

Reputation:

Do not cast, but provide one (and only one) implicit conversion constructor or conversion operator. In your case it might be as trivial as operator T () const { return _data; }

Upvotes: 1

Related Questions