sya*_*yam 8 c++ templates sfinae explicit-specialization c++11
作为我上一个问题的后续,我试图检测是否存在需要显式特化的模板函数.
我当前的工作代码检测非模板函数(感谢DyP的帮助),前提是它们至少使用一个参数,以便可以使用从属名称查找:
// switch to 0 to test the other case
#define ENABLE_FOO_BAR 1
namespace foo {
#if ENABLE_FOO_BAR
int bar(int);
#endif
}
namespace feature_test {
namespace detail {
using namespace foo;
template<typename T> decltype(bar(std::declval<T>())) test(int);
template<typename> void test(...);
}
static constexpr bool has_foo_bar = std::is_same<decltype(detail::test<int>(0)), int>::value;
static_assert(has_foo_bar == ENABLE_FOO_BAR, "something went wrong");
}
Run Code Online (Sandbox Code Playgroud)
(ENABLE_FOO_BAR宏仅用于测试目的,在我的实际代码中我没有这样的宏可用,否则我不会使用SFINAE)
当模板函数的模板参数可以由编译器自动推导时,这也适用于模板函数:
namespace foo {
#if ENABLE_FOO_BAR
template<typename T> int bar(T);
#endif
}
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试检测需要显式特化的模板函数时,存在时才会static_assert启动:foo::bar()
namespace foo {
#if ENABLE_FOO_BAR
template<typename T, typename U> T bar(U);
#endif
}
//...
// error: static assertion failed: something went wrong
Run Code Online (Sandbox Code Playgroud)
显然,编译器无法推断出模板参数,bar()因此检测失败.我尝试通过明确专门化调用来修复它:
template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
// explicit specialization ^^^^^^^^
Run Code Online (Sandbox Code Playgroud)
这在foo::bar()存在时正常工作(正确检测到该功能)但现在当foo::bar()不存在时,所有地狱都会松动:
error: ‘bar’ was not declared in this scope
template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
^
error: expected primary-expression before ‘int’
template<typename T> decltype(bar<int, T>(std::declval<T>())) test(int);
^
// lots of meaningless errors that derive from the first two
Run Code Online (Sandbox Code Playgroud)
看来我对显式特化的尝试失败了,因为编译器不知道这bar是一个模板.
我会饶恕你试图解决这个问题的所有内容并直截了当地说明:如何检测一个函数的存在,例如template<typename T, typename U> T bar(U);需要显式特化才能实例化?
以下内容可能对您有帮助:
// Helper macro to create traits to check if function exist.
// Note: template funcName should exist, see below for a work around.
#define HAS_TEMPLATED_FUNC(traitsName, funcName, Prototype) \
template<typename U> \
class traitsName \
{ \
typedef std::uint8_t yes; \
typedef std::uint16_t no; \
template <typename T, T> struct type_check; \
template <typename T = U> static yes &chk(type_check<Prototype, &funcName>*); \
template <typename > static no &chk(...); \
public: \
static bool const value = sizeof(chk<U>(0)) == sizeof(yes); \
}
Run Code Online (Sandbox Code Playgroud)
因此,提供有bar和没有提供的命名空间bar2
// namespace to test
namespace foo {
template<typename T, typename U> T bar(U);
// bar2 not present
}
Run Code Online (Sandbox Code Playgroud)
bar<int, int>检查和是否存在的代码bar2<int, int>。
// dummy class which should be never used
namespace detail {
struct dummy;
}
// Trick, so the names exist.
// we use a specialization which should never happen
namespace foo {
template <typename T, typename U>
std::enable_if<std::is_same<detail::dummy, T>::value, T> bar(U);
template <typename T, typename U>
std::enable_if<std::is_same<detail::dummy, T>::value, T> bar2(U);
}
#define COMMA_ , // trick to be able to use ',' in macro
// Create the traits
HAS_TEMPLATED_FUNC(has_foo_bar, foo::bar<T COMMA_ int>, int(*)(int));
HAS_TEMPLATED_FUNC(has_foo_bar2, foo::bar2<T COMMA_ int>, int(*)(int));
// test them
static_assert(has_foo_bar<int>::value, "something went wrong");
static_assert(!has_foo_bar2<int>::value, "something went wrong");
Run Code Online (Sandbox Code Playgroud)