因此,如果成员函数存在,我非常熟悉测试范例。目前此代码有效:
#include <iostream>
#include <type_traits>
struct has_mem_func_foo_impl {
template <typename U, U>
struct chk { };
template <typename Class, typename Arg>
static std::true_type has_foo(chk<void(Class::*)(Arg), &Class::foo>*);
template <typename, typename>
static std::false_type has_foo(...);
};
template <typename Class, typename Arg>
struct has_mem_func_foo : decltype(has_mem_func_foo_impl::template has_foo<Class,Arg>(nullptr)) { };
struct bar {
void foo(int) { }
};
int main() {
static_assert( has_mem_func_foo<bar, int>::value, "bar has foo(int)" );
}
不幸的是,如果我稍作调整:
#include <iostream>
#include <type_traits>
struct has_mem_func_foo_impl {
template <typename U, U>
struct chk { };
template <typename Class, typename... Arg>
static std::true_type has_foo(chk<void(Class::*)(Arg...), &Class::foo>*);
template <typename, typename...>
static std::false_type has_foo(...);
};
template <typename Class, typename... Arg>
struct has_mem_func_foo : decltype(has_mem_func_foo_impl::template has_foo<Class,Arg...>(nullptr)) { };
struct bar {
void foo(int) { }
};
int main() {
static_assert( has_mem_func_foo<bar, int>::value, "bar has foo(int)" );
}
我的静态断言失败了。我的印象是,当扩展到他们的位置时,可变参数模板参数包被视为相同。 gcc和clang都会产生失败的静态断言。
因此,我的问题的真正根源是这种标准行为吗?在测试是否存在可变参数模板成员函数时,它也会失败。
答案 0 :(得分:8)
我看到的问题是Arg...
传递int
是不够的。编译器在其末尾添加新的args是有效的。
无法从nullptr_t
推断添加到其末尾的内容,因此编译器会说“我放弃,而不是这种情况”。
But we don't need to have Arg...
in a deducable context for your trick to work:
#include <iostream>
#include <type_traits>
template<class Sig>
struct has_mem_func_foo_impl;
template<class R, class...Args>
struct has_mem_func_foo_impl<R(Args...)> {
template <typename U, U>
struct chk { };
template <typename Class>
static constexpr std::true_type has_foo(chk<R(Class::*)(Args...), &Class::foo>*) { return {}; }
template <typename>
static constexpr std::false_type has_foo(...) { return {}; }
};
template <typename Class, typename Sig>
struct has_mem_func_foo :
decltype(has_mem_func_foo_impl<Sig>::template has_foo<Class>(nullptr))
{};
struct bar {
void foo(int) { }
};
int main() {
static_assert( has_mem_func_foo<bar, void(int)>::value, "bar has foo(int)" );
}
我们将Args...
移动到类本身,然后只将类型传递给函数。这会阻止演绎,这使nullptr
转换为成员函数指针可行,并且事情再次起作用。
我还包括一些基于签名的改进语法,这也意味着它支持返回类型匹配。
请注意,您可能会提出错误的问题。您在询问是否存在具有特定签名的成员函数:通常您想知道的是,是否存在可以使用某组参数调用的成员函数,其返回类型与您的返回值兼容。
namespace details {
template<class T, class Sig, class=void>
struct has_foo:std::false_type{};
template<class T, class R, class... Args>
struct has_foo<T, R(Args...),
typename std::enable_if<
std::is_convertible<
decltype(std::declval<T>().foo(std::declval<Args>()...)),
R
>::value
|| std::is_same<R, void>::value // all return types are compatible with void
// and, due to SFINAE, we can invoke T.foo(Args...) (otherwise previous clause fails)
>::type
>:std::true_type{};
}
template<class T, class Sig>
using has_foo = std::integral_constant<bool, details::has_foo<T, Sig>::value>;
尝试调用T.foo(int)
,并检查返回值是否兼容。
为了好玩,我将has_foo
的类型实际上设为true_type
或false_type
,而不是继承。我可以:
template<class T, class Sig>
using has_foo = details::has_foo<T, Sig>;
如果我不想要这个额外的功能。