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