I would like to create a templated class test<T> which I can convert into test<U> (possibly implicitly) if T is convertible to U. The simplest idea that came to my mind is to add a templated constructor that takes a test<U>, where the template argument U is controlled by enable_if.
#include <iostream>
#include <type_traits>
template <typename T>
class test {
int _foo;
public:
int foo() const { return _foo; }
test() : _foo(5) {}
// This works:
// template <typename U>
// test(test<U> other)
// : _foo(other.foo()) {}
// This doesn't, can't resolve `U`:
template <typename U>
test(test<typename std::enable_if<std::is_convertible<U, T>::value, U>::type> other)
: _foo(other.foo()) {}
};
int main() {
test<int> a;
test<long> b = a; // T = long, U = int
// true
std::cout << std::boolalpha << std::is_convertible<int, long>::value << std::endl;
return 0;
}
If I just declare the first templated constructor, the code works fine. With the second constructor, it doesn't compile:
21:9: note: candidate template ignored: couldn't infer template argument 'U'
test(test<typename std::enable_if<std::is_convertible<U, T>::value, U>::type> other)
^
Why can't the compiler infer U in this case? It looks quite straightforward, I must be missing something in the template deduction. Also, if the conversion cannot be achieved in this way, what would be the best way of making test<T> convertible into test<U>?
As a side note, I managed to get it to work by making all test<T> friends, and declaring a conversion operator which uses enable_if in the implementation (as follows), but I still would like to know why the first, simpler approach is not working.
template <typename T>
class test {
int _foo;
template <typename U>
friend class test;
test(int foo) : _foo(foo) {}
public:
int foo() const { return _foo; }
test() : _foo(5) {}
template <typename U>
operator test<U>() const {
using return_t = typename std::enable_if<std::is_convertible<U, T>::value, U>::type;
return test<return_t>(_foo);
}
};