donturner
donturner

Reputation: 19146

How should I convert between classes created using C++ templates?

I have a template SoundRecording.h:

template <typename T>
class SoundRecording {
public:
    explicit SoundRecording(T init);
private:
    T data;
};

template <typename T>
SoundRecording<T>::SoundRecording(T init){
    data = init;
}

And I can create instances of this template class like this:

SoundRecording<int16_t> recording(INT16_MAX);

What is the best way to convert recording to a SoundRecording<float>?

I know I could use a simple method, for example I could declare:

SoundRecording<float> convertInt16ToFloat(SoundRecording<int16_t> input)

But I was wondering whether there was a more elegant way to achieve this using assignment or constructor operators.

Updated following comments: I am looking to define an explicit conversion. In the above example recording.data is equal to INT16_MAX after construction. After being converted to a float it should equal 1.0F.

Upvotes: 1

Views: 77

Answers (2)

vsoftco
vsoftco

Reputation: 56547

You can have a templated conversion operator like

template<class U>
explicit operator SoundRecording<U>() { /*do the conversion here*/ }

Minimal code snippet that exemplifies the technique:

template<class T>
struct X
{
    template<class U>
    explicit operator X<U>() {return {};}
};

int main()
{
    X<int> x;
    auto y = static_cast<X<float>>(x); // conversion
}

Live on Coliru

As @Mooing Duck remarked in the comment, try to mark your conversion operators as explicit to avoid nasty un-wanted compiler-triggerd conversions.

You can go a bit further and enable your conversion only when T is convertible to U or vice-versa, via a combination of std::enable_if and std::is_convertible, like so:

template<class U, 
         typename std::enable_if<std::is_convertible<T, U>::value>::type* = nullptr>
explicit operator X<U>() {return {};}

Upvotes: 4

Jonesinator
Jonesinator

Reputation: 4216

For an example of allowing this with a copy constructor instead of a casting operator:

#include <cstdint>

template <typename T>
class SoundRecording {
public:
    SoundRecording();

    template <typename U>
    SoundRecording(const SoundRecording<U>& other);

private:
    T *data;

    template <typename U>
    friend class SoundRecording;
};

template <typename T>
SoundRecording<T>::SoundRecording(){
    data = new T[10];
}

template <typename T>
template <typename U>
SoundRecording<T>::SoundRecording(const SoundRecording<U>& other){
    data = new T[10];
    for (int i = 0; i < 10; ++i) {
        data[i] = static_cast<T>(other.data[i]);
    }
}

int main(){
    SoundRecording<int16_t> recording16;
    SoundRecording<float> recordingFloat(recording16);
}

Upvotes: 2

Related Questions