我想创建一个模板化的类test<T>
,如果test<U>
可以转换为T
,我可以将其转换为U
(可能隐式)。我想到的最简单的想法是添加一个带有test<U>
的模板化构造函数,其中模板参数U
由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;
}
如果我只是声明第一个模板化构造函数,代码工作正常。使用第二个构造函数,它不会编译:
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)
^
为什么编译器在这种情况下不能推断出U
?它看起来很简单,我必须在模板推导中遗漏一些东西。此外,如果无法通过这种方式实现转化,将test<T>
转换为test<U>
的最佳方法是什么?
作为旁注,我设法通过制作所有test<T>
朋友并声明在实现中使用enable_if
的转换运算符(如下所示)来实现它,但我仍然希望要知道为什么第一种更简单的方法不起作用。
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);
}
};
答案 0 :(得分:3)
U
出现在non-deduced context。
这解决了编译器错误:
template <typename U, typename = typename std::enable_if<std::is_convertible<U, T>::value, U>::type>
test(test<U> other)
: _foo(other.foo()) {}
答案 1 :(得分:2)
为什么编译器在这种情况下不能推断出来?
类型扣除从根本上无法从T
等参数类型推断a<T>::b
。即使你有
template <typename T> struct identity { typedef T type; };
然后编译器仍然不能排除你在某个类型identity<X>::type
偷偷地制作Y
X
的地方提供专业化。
与std::enable_if
相同:标准库类模板在语言规则中没有得到特殊处理,因此编译器无法弄清楚如果std::enable_if<cond, U>::type
应该是是X
,然后U
必须是X
。
这就是常规函数std::enable_if
通常出现在返回类型中的原因。由于与构造函数相同的原因,它不能出现在参数中。它不能位于模板参数中,因为调用者可以指定其他类型并绕过您的限制。但返回类型是安全的。
构造函数没有返回类型。幸运的是,调用者无法显式指定构造函数模板参数,因此将std::enable_if
放入模板默认参数中已经由m.s.回答。那里很安全。