让我们检查是否
struct Thing {
int foo(double, bool) {return 0;}
};
在编译期间具有int foo(double, bool)
成员函数。有很多方法可以做到这一点,而且大多数只是其他方式的变体。有人会想到一种与我在这里提到的5种方式截然不同(或至少相当有创意)的方式吗?我只是想学习一些模板和SFINAE的新技术。
#include <iostream>
#include <type_traits>
// Using void_t (this includes using std::is_detected).
template <typename T>
using void_t = void;
template <typename T, typename = void>
struct has_foo : std::false_type {};
template <typename T>
struct has_foo<T,
void_t<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>
> : std::true_type {};
// Using the ... default argument.
template <typename T>
struct hasfoo {
template <typename U>
static std::true_type test (decltype(static_cast<int(T::*)(double, bool)>(&T::foo))*); // or 'decltype(static_cast<int>(std::declval<U>().foo(double{}, bool{})))*' works fine too.
template <typename>
static std::false_type test (...);
static constexpr bool value = decltype(test<T>(nullptr))::value;
};
// Overloads and trailing return types.
template <typename>
struct Helper : std::true_type {};
template <typename T>
auto helper(int) -> Helper<decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})))>;
template <typename>
std::false_type helper(long);
template <typename T>
constexpr bool hasFoo() {return decltype(helper<T>(0))::value;}
// Comma operator (basically the same as the above).
template <typename T>
auto check(int) -> decltype(static_cast<int>(std::declval<T>().foo(double{}, bool{})), std::true_type{});
template <typename T>
std::false_type check(...);
template <typename T>
using HasFoo = decltype(check<T>(0));
// Member function pointer template parameter.
template <typename T>
struct Hasfoo {
template <typename U, int(U::*)(double, bool)>
struct Tag;
template <typename U>
static constexpr bool test (Tag<U, &U::foo>*) {return true;}
template <typename>
static constexpr bool test (...) {return false;}
static constexpr bool value = test<T>(nullptr);
};
// Tests
struct Thing {
int foo(double, bool) {return 0;}
};
int main() {
static_assert (has_foo<Thing>::value, "");
static_assert (hasfoo<Thing>::value, "");
static_assert (hasFoo<Thing>(), "");
static_assert (HasFoo<Thing>::value, "");
}
编辑:我刚刚想起了一个优雅且更通用的解决方案,Yakk很久以前就提出了一个不同的问题(这是他的实际打字,只是为了匹配foo功能而修改):
namespace meta {
namespace details {
template<template<class...>class Z, class=void, class...Ts>
struct can_apply : std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, decltype((void)(std::declval<Z<Ts...>>())), Ts...>:
std::true_type
{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
}
template<class T>
using member_foo = decltype(static_cast<int(T::*)(double, bool)>(&T::foo));
template<class T>
using has_member_foo = meta::can_apply<member_foo, T>;
答案 0 :(得分:1)
有人会想到一种与我在这里提到的5种方式截然不同(或至少相当具有创造性)的方式吗?
一种相当有创意的方法可能是下面的方法
它基于sizeof
运算符和一个简单的使用声明(此处名为Type
)。
其余的SFINAE和部分模板专业化。
它遵循一个最小的工作示例:
#include<type_traits>
#include<cstddef>
template<typename T, std::size_t>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, sizeof(static_cast<void(T::*)(int)>(&T::f))>>: std::true_type {};
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<R>::value, "!");
static_assert(not U<T>::value, "!");
}
答案 1 :(得分:1)
有人会想到一种与我在这里提到的5种方式截然不同(或至少相当具有创造性)的方式吗?
一种相当有创意的方法可能是下面的方法
它基于noexcept
运算符和一个简单的使用声明(此处名为Type
)。
其余的SFINAE和部分模板专业化。
它遵循一个最小的工作示例:
#include<type_traits>
#include<utility>
template<typename T, bool>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, noexcept(std::declval<T>().f(42))>>: std::true_type {};
struct S { void f(int) {} };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<T>::value, "!");
}
如果与其他解决方案相比,此解决方案会出现问题 举个例子,下面的结构也会通过测试:
struct R { int f(double) {} };
换句话说,只要要测试的函数接受一个可以转换为42的类型的参数,无论返回类型是什么,都可以接受。
答案 2 :(得分:1)
有人会想到一种与我在这里提到的5种方式截然不同(或至少相当具有创造性)的方式吗?
一种相当有创意的方法可能是下面的方法
它没有利用任何未评估的背景。相反,它依赖于一个简单的使用声明(这里名为Type
),这就是全部。
其余的SFINAE和部分模板专业化。
它遵循一个最小的工作示例:
#include<type_traits>
template<typename T, void(T::*)(int)>
using Type = T;
template<typename T, typename = T>
struct U: std::false_type {};
template<typename T>
struct U<T, Type<T, &T::f>>: std::true_type {};
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(U<S>::value, "!");
static_assert(not U<R>::value, "!");
static_assert(not U<T>::value, "!");
}
答案 3 :(得分:1)
有人会想到一种与我在这里提到的5种方式截然不同(或至少相当具有创造性)的方式吗?
一种相当有创意的方法可能是下面的方法 它基于功能模板和重载。如果您知道标签调度的工作原理,那么理解它应该非常简单。
它遵循一个最小的工作示例:
#include<type_traits>
#include<cstddef>
template<typename T, void(T::*)(int) = &T::f>
constexpr std::true_type f(int) { return {}; }
template<typename T>
constexpr std::false_type f(char) { return {}; }
template<typename T>
constexpr auto detect() { return f<T>(0); }
struct S { void f(int) {} };
struct R { int f(double) { return 42; } };
struct T {};
int main() {
static_assert(detect<S>(), "!");
static_assert(not detect<R>(), "!");
static_assert(not detect<T>(), "!");
}
答案 4 :(得分:0)
有人会想到一种与我在这里提到的5种方式截然不同(或至少相当具有创造性)的方式吗?
如果你可以使用C ++ 14,另一种检查类是否有成员变量的方法是模板变量。左右 举个例子:
template<typename T, typename = void>
constexpr bool has_foo = false;
template<typename T>
constexpr bool has_foo<T, decltype(std::declval<T>().foo(), void())> = true;
我想这可能至少是一个更紧凑的解决方案。
它遵循一个最小的工作示例:
#include<utility>
template<typename T, typename = void>
constexpr bool has_f = false;
template<typename T>
constexpr bool has_f<T, decltype(std::declval<T>().f(0), void())> = true;
struct S { void f(int) {} };
struct T {};
int main() {
static_assert(has_f<S>, "!");
static_assert(not has_f<T>, "!");
}