通过继承检测习语和 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
我可以使用 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*>
。请注意,没有选项可以同时包含它派生自