c++ - 检查模板化成员函数是否存在 SFINAE
问题描述
以下问题:我想检查模板化方法是否存在,因此我调整了此处给出的示例: 是否可以编写模板来检查函数是否存在?
#include <cstdio>
#include <type_traits>
#define CHECK4_MEMBER_FUNC(RETTYPE,FUNCTION,...) \
template <class ClassType> \
class CIfCheck_##FUNCTION \
{\
private: \
template <class MemberPointerType> \
static std::true_type testSignature(RETTYPE (MemberPointerType::*)(__VA_ARGS__)); \
\
template <class MemberPointerType> \
static std::false_type testExistence(...); \
\
template <class MemberPointerType> \
static decltype(testSignature(&MemberPointerType::FUNCTION)) testExistence(std::nullptr_t); \
public: \
using type = decltype(testExistence<ClassType>(nullptr));\
static const bool value = type::value; \
};
class Bla
{
public:
template <typename SomeType>
bool init(SomeType someValue)
{
///
return true;
}
void exec()
{
return;
}
};
CHECK4_MEMBER_FUNC(bool, init, int);
CHECK4_MEMBER_FUNC(void, exec, void);
int main()
{
Bla blaObj;
blaObj.init<int>(2);
static_assert(CIfCheck_exec<Bla>::value, "no exec");
static_assert(CIfCheck_init<Bla>::value, "no init");
return 0;
}
但不幸的static_assert()
是,触发了init()
(因为特化可能会在稍后实例化对象时进行评估main()
)。
我尝试了明确的成员专业化,但它仍然失败:
template<>
bool Bla::init<int>(int item)
{
int temp = item*2; // do with item something
return false;
}
PS:附带问题(可能另一个问题主题会更有意义:
std::false_type testExistence(...);
为什么我必须在这里传递一个论点?如果我删除可变参数...
选项(和nullptr
and nullptr_t
),编译器会由于testExistence()
.
解决方案
但不幸的是,为 init 触发了 static_assert(因为在 main() 中实例化对象时,可能会在稍后评估特化)
不完全是。
问题是这init()
是一个模板方法,所以当你写
decltype(testSignature(&MemberPointerType::FUNCTION))
没有选择指针,因为编译器无法选择正确的方法。
你可以试试
decltype(testSignature(&MemberPointerType::template FUNCTION<__VA_ARGS__>))
但现在不适用于exec()
那不是模板方法
要同时使用模板和非模板方法...不是简单地通过可变参数宏传递,因为可变参数部分不能为空...但我建议如下
template <typename...>
struct wrap
{ };
#define CHECK4_MEMBER_FUNC(RETTYPE,FUN,...) \
template <class ClassType> \
class CIfCheck_##FUN \
{\
private: \
template <typename MPT> \
static auto testSig (wrap<void>) \
-> std::is_same<decltype(std::declval<MPT>().FUN()),\
RETTYPE>; \
\
template <typename MPT, typename ... As> \
static auto testSig (wrap<As...>) \
-> std::is_same<decltype(std::declval<MPT>().FUN(std::declval<As>()...)), \
RETTYPE>; \
\
template <typename...> \
static std::false_type testSig (...);\
\
public: \
using type = decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));\
static const bool value = type::value; \
};
请注意,我添加了一个wrap
结构来包装模板参数类型;通常使用std::tuple
,但在这种情况下,我们需要wrap<void>
因为std::tuple<void>
给出错误。
另请注意,我的解决方案从另一个角度来看是不同的(根据您的具体需求,可能更好或更糟):您的解决方案检查是否存在具有完全签名的方法;我的解决方案检查是否存在可使用给定参数列表调用的方法。
具体例子:假设有一个Bla::foo()
方法接受一个long
值
void foo (long)
{ }
使用您的解决方案,如果您检查int
参数
CHECK4_MEMBER_FUNC(void, foo, int);
static_assert( false == CIfCheck_foo<Bla>::value, "no foo with int");
你得到一个false
值是CIfCheck_foo
因为没有类型Bla
的方法(有一个不同的方法)。foo
void(&BLA::*)(int)
void(&BLA::*)(long)
使用我的方法,您可以从中获得一个true
值,CIfCheck_foo
因为foo(long)
接受也是一个int
值(并且返回的类型是void
)。
std::false_type testExistence(...);
为什么我必须在这里传递一个论点?如果我删除可变参数
...
选项(和nullptr
andnullptr_t
),编译器会由于testExistence()
.
那testExistence()
,作为
template <typename...> \
static std::false_type testSig (...);\
是第二个选择。
我的意思是......当你testExistence()
在里面进行宏调用时decltype()
decltype(testExistence<ClassType>(nullptr));
testSig()
或者我在里面的宏调用decltype()
decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));
nullptr
使用参数 (或)调用该函数wrap<__VA_ARGS__>{}
。
当第一个选项可用时(RETTYPE (MemberPointerType::*)(__VA_ARGS__)
在您的情况下何时出现,在我的示例中何时可以调用具有所需参数的方法),编译器选择该版本并返回std::true_type
(或std::is_same
在我的代码中)。
但是当第一选择不可用时?
第二个选择,返回的版本std::false
,是必需的。但是电话是有论据的。这里的省略号是旧的 C 风格的可变参数列表并接受零个或多个参数,因此也接受一个参数。
如果您删除省略号 ( ...
),则第二选择不能再接受参数(成为零参数函数)并且您会收到编译错误,因为编译器找不到与参数兼容的第二选择函数。
推荐阅读
- sql - 天蓝色突触分区表 - 没有性能提升
- python - 使用 HMAC 为 get 请求签名散列有效负载不起作用
- angular - 在 mat-tab-group 上制作粘性标题
- amazon-web-services - 如何在 Redshift 中的时间列中插入时间值?
- apache - .htaccess - 将域/语言/重定向到每种语言的子域
- javascript - 如何显示和隐藏表单?
- r - 除了最好的 Dirichlet 组件之外,是否可以检索?
- stored-procedures - 如何保持 tblPurchase 和 tblProductStock 表不丢失。(我需要保持表和值永久不变)
- python - 当我们在 groupby 之后绘制数据框时,为什么 pandas 会为每个组生成一个图形句柄?
- ios - 如何在 swift pod 库中添加 .framework(在 obj-c 中完成)