检测功能失败的习语 static_assert
Detect idiom with function failing static_assert
有没有一种方法可以使用 detection idiom(或其他方法)来测试一个函数对于给定的模板参数是否有效,如果它由于 static_assert
而失败?
下面的示例说明 foo
(失败的 return 类型计算)的有效性被按预期检测到,但 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, Op, 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 而言声明是可以的。
如果你写了一个真正的检测惯用语(如),并像这样使用它:
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
的声明类型将无法通过 void
的 SFINAE
static_assert
的要点是如果没有找到其他更好的匹配,编译将失败,而不是作为 SFINAE 的替代。
Andy 的回答是正确的,他解释说 sfinae 无法检测到 static_assert 或任何定义替换失败。不过,我想指出的是,您的问题是可以解决的,尽管您需要承受一些重复的冲击。
基本上,您需要找出 lexical_cast
尝试将哪种操作应用于泛型类型。然后你用你自己的函数包装 lexical_cast
,并在 lexical_cast
需要的任何属性上构建你的函数。这不是很优雅,但我怀疑对与您感兴趣的应用程序相关的类型有不止一些要求,因此它是一个实用的解决方案(也许)。
也许这是显而易见的,但我想我会提到这一点,因为它尚未涵盖。
有没有一种方法可以使用 detection idiom(或其他方法)来测试一个函数对于给定的模板参数是否有效,如果它由于 static_assert
而失败?
下面的示例说明 foo
(失败的 return 类型计算)的有效性被按预期检测到,但 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, Op, 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 而言声明是可以的。
如果你写了一个真正的检测惯用语(如
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
的声明类型将无法通过 void
static_assert
的要点是如果没有找到其他更好的匹配,编译将失败,而不是作为 SFINAE 的替代。
Andy 的回答是正确的,他解释说 sfinae 无法检测到 static_assert 或任何定义替换失败。不过,我想指出的是,您的问题是可以解决的,尽管您需要承受一些重复的冲击。
基本上,您需要找出 lexical_cast
尝试将哪种操作应用于泛型类型。然后你用你自己的函数包装 lexical_cast
,并在 lexical_cast
需要的任何属性上构建你的函数。这不是很优雅,但我怀疑对与您感兴趣的应用程序相关的类型有不止一些要求,因此它是一个实用的解决方案(也许)。
也许这是显而易见的,但我想我会提到这一点,因为它尚未涵盖。