检查模板化成员函数是否存在 SFINAE
Check if templated member function exists SFINAE
以下问题:
我想检查模板化方法是否存在,因此我修改了此处给出的示例:
Is it possible to write a template to check for a function's existence?
#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;
}
P.S.: 附带问题(可能另一个问题主题会更有意义:
std::false_type testExistence(...);
为什么我必须在这里传递一个参数?如果我删除可变参数 ...
选项(以及 nullptr
和 nullptr_t
),编译器会由于 testExistence()
.
的不明确存在而出错
but unfortunately the static_assert is triggered for init (as the specialization is probably evaluated at a later time as the object is being instantiated in main())
不完全是。
问题是init()
是一个模板方法,所以当你写
decltype(testSignature(&MemberPointerType::FUNCTION))
没有指针被 select 编辑,因为编译器无法 select 正确的方法。
你可以试试
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");
你从 CIfCheck_foo
得到一个 false
值,因为 Bla
中没有 void(&BLA::*)(int)
类型的方法 foo
(有一个 void(&BLA::*)(long)
那是不一样的)。
用我的方法你可以从 CIfCheck_foo
得到一个 true
值,因为 foo(long)
也接受一个 int
值( returned 类型是 void
).
std::false_type testExistence(...);
Why exactly do I have to pass an argument here? If I remove the variadic argument ...
option (and nullptr
and nullptr_t
), the compiler errors due to ambiguous existence of testExistence()
.
即testExistence()
,如
template <typename...> \
static std::false_type testSig (...);\
第二选择
我的意思是...当您在 decltype()
中调用 testExistence()
宏时
decltype(testExistence<ClassType>(nullptr));
或者我在 decltype()
中调用 testSig()
的宏
decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));
调用带参数的函数(nullptr
或 wrap<__VA_ARGS__>{}
)。
当第一个选择可用时(在您的情况下出现 RETTYPE (MemberPointerType::*)(__VA_ARGS__)
,在我的示例中何时可以调用具有所需参数的方法),编译器选择该版本并 return std::true_type
(或我的代码中的 std::is_same
)。
但是当第一选择不可用时?
第二选择,版本returning std::false
,是必需的。但这个电话是有争议的。这里的省略号是旧的 C 风格可变参数列表并接受零个或多个参数,因此也接受一个参数。
如果您删除省略号 (...
),第二个选择将不再接受参数(变为零参数函数)并且您会收到编译错误,因为编译器找不到第二个选择函数与参数兼容。
以下问题: 我想检查模板化方法是否存在,因此我修改了此处给出的示例: Is it possible to write a template to check for a function's existence?
#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;
}
P.S.: 附带问题(可能另一个问题主题会更有意义:
std::false_type testExistence(...);
为什么我必须在这里传递一个参数?如果我删除可变参数 ...
选项(以及 nullptr
和 nullptr_t
),编译器会由于 testExistence()
.
but unfortunately the static_assert is triggered for init (as the specialization is probably evaluated at a later time as the object is being instantiated in main())
不完全是。
问题是init()
是一个模板方法,所以当你写
decltype(testSignature(&MemberPointerType::FUNCTION))
没有指针被 select 编辑,因为编译器无法 select 正确的方法。
你可以试试
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");
你从 CIfCheck_foo
得到一个 false
值,因为 Bla
中没有 void(&BLA::*)(int)
类型的方法 foo
(有一个 void(&BLA::*)(long)
那是不一样的)。
用我的方法你可以从 CIfCheck_foo
得到一个 true
值,因为 foo(long)
也接受一个 int
值( returned 类型是 void
).
std::false_type testExistence(...);
Why exactly do I have to pass an argument here? If I remove the variadic argument
...
option (andnullptr
andnullptr_t
), the compiler errors due to ambiguous existence oftestExistence()
.
即testExistence()
,如
template <typename...> \
static std::false_type testSig (...);\
第二选择
我的意思是...当您在 decltype()
testExistence()
宏时
decltype(testExistence<ClassType>(nullptr));
或者我在 decltype()
testSig()
的宏
decltype(testSig<ClassType>(wrap<__VA_ARGS__>{}));
调用带参数的函数(nullptr
或 wrap<__VA_ARGS__>{}
)。
当第一个选择可用时(在您的情况下出现 RETTYPE (MemberPointerType::*)(__VA_ARGS__)
,在我的示例中何时可以调用具有所需参数的方法),编译器选择该版本并 return std::true_type
(或我的代码中的 std::is_same
)。
但是当第一选择不可用时?
第二选择,版本returning std::false
,是必需的。但这个电话是有争议的。这里的省略号是旧的 C 风格可变参数列表并接受零个或多个参数,因此也接受一个参数。
如果您删除省略号 (...
),第二个选择将不再接受参数(变为零参数函数)并且您会收到编译错误,因为编译器找不到第二个选择函数与参数兼容。