通过继承检测习语和 SFINAE

Detection Idiom and SFINAE via Inheritance

我可以使用 std::experimental::is_detected 检查 chips1_t 是否可以用 float* 实例化(它不能),如下面的 static_assert 所示:

#include <experimental/type_traits>
#include <type_traits>

template <typename, typename = void>
  struct chips1;

template <typename T>
struct chips1<T*,std::enable_if_t<std::is_same<T,int>::value>> {
  using type = int;
};

template <typename T> using chips1_t = typename chips1<T>::type;

static_assert(!std::experimental::is_detected<chips1_t,float*>::value,"");

如果我随后尝试使用 chips2_t 进行检查,类似的 static_assert,如下所示,将产生编译错误;关于失踪的 type 成员。谁能告诉我为什么?

struct Base {};

template <typename>
  struct chips2;

template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
  using type = int;
};

template <typename T> using chips2_t = typename chips2<T>::type;

static_assert(!std::experimental::is_detected<chips2_t,float*>::value,"");

在第二种情况下,基class不是模板类型的一部分,因此在SFINAE期间不考虑(它是一个依赖基class)

实例化

template <typename T>
struct chips2<T*>

成功,然后编译失败,因为它派生自 std::enable_if_t<std::is_same<T,int>::value,Base>,它变成了一个格式错误的表达式。


您不能根据模板的基数来特化模板 class*.

例如,您不能拥有两个相互竞争的专业,例如:

template <typename T>
struct chips2<T*> : std::enable_if_t<std::is_same<T,int>::value,Base> {
  using type = int;
};

template <typename T>
struct chips2<T*> : std::enable_if_t<!std::is_same<T,int>::value,Base> {
  using type = float;
};

从专业化的角度来看,它们将被认为是相同的(即,从命名的角度来看,它们是相同的,因此编译器会将第二个视为对第一个的重新声明)。唯一重要的部分是冒号之前的所有内容 :

在第一种情况下,您的 enable_if 直接是模板专业化的一部分,因此 SFINAE 可以正常工作。


另一个笔记

在第二个中,您实际上已经无法使用 int* 以外的任何实例来实例化 chips2,请参见以下示例:

struct Base {};

template <typename>
struct chips2
{};

template <typename T>
struct chips2<T*> 
   : std::enable_if_t<std::is_same<T,int>::value, Base> 
{};

int main(){
    chips2<float*> c;
}

你可能会倾向于认为 SFINAE 会为 c 选择基础 class 模板,但实际上它选择专业化 chips2<T*> 因为我上面说了,和编译失败。


*相关标准在 [temp.spec]

the name of the class that is explicitly specialized shall be a simple-template-id.

其中 simple-template-id 的形式为:
模板名称<模板参数列表选项>

例如chips2<T*>。请注意,没有选项可以同时包含它派生自

的 class