std :: is_convertible中的std :: decay是否冗余?

问题描述

我写了这样的代码

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

衰减_t实际做什么?

  • 删除顶级const / volatile限定符
  • 删除顶级参考限定符
  • 数组->指针转换
  • function->函数指针转换

可能值得注意:衰减_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上会有所不同。

现场观看 https://godbolt.org/z/P5P64Y