std::decay 在 std::is_convertible 中是多余的吗?
Is std::decay redundant in std::is_convertible?
我写了这样的代码:
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) {}
};
我希望我的 class 的用户只能使用可转换为 T
的类型调用 f
。
那里std::decay
多余吗?如果我删除它可能会错过一些特殊情况?
我认为您的问题更具哲学性,例如:在 C++ 类型的世界中,是否存在 T 和 U 的任何情况,其中在以下调用 f() 和 g() 之间存在明显差异class:
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实际上是做什么的?
- 删除 top-level const/volatile 限定符
- 删除 top-level 引用限定符
- 数组->指针转换
- 函数 -> 函数指针转换
可能值得注意:decay_t 是根据传递给函数时函数参数类型发生的情况建模的。 因此,decay_t<U>
应始终等同于 U(前提是模板推导机制未被显式模板参数破坏。)
因此我们只需要关注decay_t<T>
并思考那些案例:
- T 可以转换为 T& 吗? (没有)
- 函数指针可以转换为函数类型吗? (没有)
- T* 可以转换为 T[] 吗? (没有)
- T 可以转换为 const T 吗? (是)
所以我们应该能够构建案例来证明这些观察结果:
// T is ARRAY type
A<int[]> a1;
int ary[] = {1,2,3};
a1.f(ary); // OK
a1.g(ary); // ERROR (U decays to T*)
// T is REFERENCE type
A<int&> a2;
a2.f(123); // OK
a2.g(123); // ERROR (U decays to int)
// T is FUNCTION type
A<void()> a3;
a3.f(foo); // OK
a3.g(foo); // ERROR (U decays to void(*)()
// T is const type
A<const int> a4;
a4.f(123); // OK
a4.g(123); // OK
所以是的,在某些情况下,衰减的值无法返回,并且由于 U 隐式衰减,当 T 未衰减时,在某些情况下您可能 运行 出现一些错误。
您可以安全地删除 U 的 decay_t
,但在 T 上它会有所不同。
我写了这样的代码:
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) {}
};
我希望我的 class 的用户只能使用可转换为 T
的类型调用 f
。
那里std::decay
多余吗?如果我删除它可能会错过一些特殊情况?
我认为您的问题更具哲学性,例如:在 C++ 类型的世界中,是否存在 T 和 U 的任何情况,其中在以下调用 f() 和 g() 之间存在明显差异class:
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实际上是做什么的?
- 删除 top-level const/volatile 限定符
- 删除 top-level 引用限定符
- 数组->指针转换
- 函数 -> 函数指针转换
可能值得注意:decay_t 是根据传递给函数时函数参数类型发生的情况建模的。 因此,decay_t<U>
应始终等同于 U(前提是模板推导机制未被显式模板参数破坏。)
因此我们只需要关注decay_t<T>
并思考那些案例:
- T 可以转换为 T& 吗? (没有)
- 函数指针可以转换为函数类型吗? (没有)
- T* 可以转换为 T[] 吗? (没有)
- T 可以转换为 const T 吗? (是)
所以我们应该能够构建案例来证明这些观察结果:
// T is ARRAY type
A<int[]> a1;
int ary[] = {1,2,3};
a1.f(ary); // OK
a1.g(ary); // ERROR (U decays to T*)
// T is REFERENCE type
A<int&> a2;
a2.f(123); // OK
a2.g(123); // ERROR (U decays to int)
// T is FUNCTION type
A<void()> a3;
a3.f(foo); // OK
a3.g(foo); // ERROR (U decays to void(*)()
// T is const type
A<const int> a4;
a4.f(123); // OK
a4.g(123); // OK
所以是的,在某些情况下,衰减的值无法返回,并且由于 U 隐式衰减,当 T 未衰减时,在某些情况下您可能 运行 出现一些错误。
您可以安全地删除 U 的 decay_t
,但在 T 上它会有所不同。