我写了这样的代码:
template <class T>
class A {
template <class U, class =
class std::enable_if_t<std::is_convertible_v<std::decay_t<U>, std::decay_t<T>>>>
void f(U&& val) {}
};
我希望我的班级的用户只能使用可转换为
f
的类型来调用 T
。
std::decay
是多余的吗?如果我删除它,也许我会错过一些特殊情况?
我认为你的问题更具哲学性,例如:在 C++ 类型的宇宙中,是否存在 T 和 U 的情况,在以下类上调用 f() 和 g() 之间存在可观察到的差异:
template <class T>
struct A {
template <
class U,
enable_if_t<is_convertible_v<decay_t<U>, decay_t<T>>>* = nullptr
>
void f(U&& val) {}
template <
class U,
enable_if_t<is_convertible_v<U, T>>* = nullptr
>
void g(U&& val) {}
};
decay_t 实际上做了什么?
可能值得注意的是:decay_t 是根据函数参数类型传递到函数时发生的情况建模的。
(已更新)
如果
U
按值取,则 decay_t<U>
将等于 U。
但是由于
U
是通用引用(在您的示例中),因此 U 可能会推断出引用类型,因此衰减它会产生可见的效果。当 T
只能从参考构造时,那么衰减它就会改变这种类型特征的答案。
Merry 的优秀例子:
#include <type_traits>
#include <utility>
template <class T>
struct A {
template <
class U,
std::enable_if_t<std::is_convertible_v<std::decay_t<U>, T>>* = nullptr
>
void f(U&& val) {}
template <
class U,
std::enable_if_t<std::is_convertible_v<U, T>>* = nullptr
>
void g(U&& val) {}
};
struct B
{
B(int &) {}
};
void foo() {}
int main() {
A<B> a1;
int x = 3;
// a1.f(x); // fails to compile: int not convertible to B
a1.g(x); // ok; int& is convertible to B
}