有没有办法使用
detection idiom(或其他方法)来测试函数是否对给定的模板参数有效,如果它因static_assert而失败?
下面的示例说明了foo(失败的返回类型计算)的有效性是按预期检测到的,而bar(失败的static_assert)的有效性则没有.
#include <iostream> #include <type_traits> template <typename... T> using void_t = void; template <class AlwaysVoid,template<class...> class Op,class... Args> struct detector: std::false_type { }; template <template<class...> class Op,class... Args> struct detector<void_t<Op<Args...>>,Op,Args...>: std::true_type { }; template <template<class...> class Op,class... Args> constexpr bool is_detected = detector<void,Args...>::value; template <typename T> std::enable_if_t<!std::is_void<T>::value> foo() { std::cout << "foo" << std::endl; } template <typename T> void bar() { static_assert( !std::is_void<T>::value ); std::cout << "bar" << std::endl; } template <typename T> using foo_t = decltype(foo<T>()); template <typename T> using bar_t = decltype(bar<T>()); int main(int argc,char* argv[]) { foo<int>(); // foo<void>(); // fails as expected bar<int>(); // bar<void>(); // fails as expected std::cout << std::boolalpha; // detection works for foo std::cout << is_detected<foo_t,int > << std::endl; // true std::cout << is_detected<foo_t,void> << std::endl; // false // but not for bar std::cout << is_detected<bar_t,int > << std::endl; // true std::cout << is_detected<bar_t,void> << std::endl; // true !!! }
这就是我无法检测boost :: lexical_cast是否对给定类型有效的原因.
解决方法
这里不可能使用SFINAE来获得正确的输出,因为
SFINAE rules在声明上运行,而不是定义.
声明的bar类型将始终为void(void),因此就SFINAE而言,声明是可以的.
如果你写了一个真正的检测成语(如I did here),并像这样使用它:
template <typename T> using CanCallFoo_t = decltype(&foo<T>); template<class T> using CanCallFoo = detect<T,CanCallFoo_t,void>; template<class T> using CanCallBar_t = decltype(&bar<T>); template< class T> using CanCallBar = detect<T,CanCallBar_t,void>; //... std::cout << CanCallFoo<int>::value << std::endl; // true std::cout << CanCallFoo<void>::value << std::endl; // false std::cout << CanCallBar<int>::value << std::endl; std::cout << CanCallBar<void>::value << std::endl;
您会注意到SFINAE成功,然后在解析定义时出现编译器错误.
error: static assertion Failed
static_assert( !std::is_void<T>::value );
请注意,它适用于foo,因为foo的声明类型将使SFINAE失效
static_assert的要点是,如果没有找到其他更好的匹配,则编译失败,而不是替换SFINAE.