【发布时间】:2016-03-30 21:27:22
【问题描述】:
让我们检查一下
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>;
【问题讨论】:
-
在列出的四个选项中,例如,只有
hasfoo会检测到从int foo(double,bool) { return 0; }到int foo(double,double) { return 0; }的更改。 -
嗯...我想我不太关心隐式转换。但是
decltype(static_cast<int(T::*)(double, bool)>(&T::foo))*可以用于所有这些。这就是我很好奇的表达方式。 -
template<class T> concept bool foo_double_bool = requires (T t) { t.foo(0.0, true); };。你需要多少种不同的方式来表达同一件事? -
@T.C.尚未邀请概念参加聚会:-)
标签: c++ templates c++11 sfinae