检测成文的工作原理如下
template<typename T, typename = void>
struct has_foo {static constexpr bool value = false;};
template<typename T>
struct has_foo<T, std::void_t<decltype(&T::foo)>> {static constexpr bool value = true;};
template<typename T>
constexpr bool has_foo_v = has_foo<T>::value;
,然后我们可以在任何类型的T
中检测到foo
的存在。
if constexpr(has_foo_v<decltype(var)>)
var.foo();
我的问题是,要输入很多(请阅读:想输入很多键盘(,我想知道是否可以使用以下内容
if constexpr(std::void_t<decltype(&decltype(var)::foo)>(), true)
var.foo();
不是。
这背后有原因吗?
更具体地说,如果允许这样做,必须进行哪些权衡?
以来C 17,如果您确实需要对sfinae进行sfinae:
,总会有一个constexpr lambda解决方法#include <utility>
template <class Lambda, class... Ts>
constexpr auto test_sfinae(Lambda lambda, Ts&&...)
-> decltype(lambda(std::declval<Ts>()...), bool{}) { return true; }
constexpr bool test_sfinae(...) { return false; }
template <class T>
constexpr bool bar(T var) {
if constexpr(test_sfinae([](auto v) -> decltype(v.foo()){}, var))
return true;
return false;
}
struct A {
void foo() {}
};
struct B { };
int main() {
static_assert(bar(A{}));
static_assert(!bar(B{}));
}
[live demo]
您将指针用于成员函数是一个坏主意。如果foo
被超载,则会伪装失败(您有一个FOO,而不仅仅是一个(。谁真的想要"你有一个foo&quot&quot?几乎没有人。
这是一个英国人版本:
template<class T>
using dot_foo_r = decltype( std::declval<T>().foo() );
template<class T>
using can_foo = can_apply<dot_foo_r, T>;
其中
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z, void, Ts...>;
现在,写dot_foo_r
有点烦人。
使用 constexpr
lambdas,我们可以使它变得不那么烦人,并在内联行动。
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
-> decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
它确实需要RETURNS
宏,至少直到 @barry提交[](auto&&f)RETURNS(f())
等于[](auto&&f)=>f()
。
然后,我们编写can_invoke
,这是std::is_invocable
的constexpr
变体:
template<class F>
constexpr auto can_invoke( F&& f ) {
return [](auto&&...args)->std::is_invocable<F(decltype(args)...)>{
return {};
};
}
这给了我们:
if constexpr(
can_invoke([](auto&&var) RETURNS(var.foo()))(var)
) {
var.foo();
}
或使用 @barry提出的C 20语法:
if constexpr(can_invoke(var=>var.foo())(var)) {
var.foo();
}
我们完成了。
诀窍是RETURNS
宏(或=>
C 20功能(使我们可以在表达式上进行sfinae。lambda成为一种将表达方式作为价值携带的简便方法。
你可以写
[](auto&&var) ->decltype(var.foo()) { return var.foo(); }
,但我认为RETURNS
值得(我不喜欢宏(。
您还可以使用std::experimental::is_detected
。
在您的示例中,代码将看起来像:
template <class T>
using has_foo_t = decltype(std::declval<T>().foo());
if constexpr(is_detected_v<has_foo_t,decltype(var)>)
var.foo();