如何检查一个类是否具有一个或多个具有给定名称的方法?

Par*_*tes 2 c++ methods templates sfinae c++14

有许多不同的技巧可用于检查类是否Foo具有名为 的方法bar。例如:

  • 如果我们关心方法签名,我们会围绕类似的东西构建一个特征std::void_t<decltype(static_cast<ing(Foo::*)(char) const>(&Foo::bar))>
  • 如果我们不关心签名,我们可以使用类似的东西std::void_t<decltype(&Foo::bar)>

如果Foo有多个名为 的方法bar,则前者继续正常工作(因为最多一个重载将与 兼容static_cast),但是,后者将中断,大概是因为decltype(&Foo::bar)变得不明确。

是否有一种技术可以检测一个类是否具有一个或多个具有给定名称的方法,而不管方法签名如何?如果它可以在 C++14 中工作,那就加分了。

示例代码(Godbolt 链接):

#include <type_traits>

template<typename T, typename = void> struct HasIntBar_t : std::false_type {};
template<typename T> struct HasIntBar_t<T, std::void_t<decltype(static_cast<int(T::*)(char)>(&T::bar))>> : std::true_type {};
template<typename T> inline constexpr bool HasIntBar = HasIntBar_t<T>::value;

template<typename T, typename = void> struct HasAnyBar_t : std::false_type {};
template<typename T> struct HasAnyBar_t<T, std::void_t<decltype(&T::bar)>> : std::true_type {};
template<typename T> inline constexpr bool HasAnyBar = HasAnyBar_t<T>::value;


////////////////////////////////////
// Test Types:
struct None {};
struct OneInt
{
    int bar(char);
};
struct OneIntInherited : public OneInt {};
struct OneDouble
{
    double bar() const;
};
struct OneDoubleInherited : public OneDouble {};
struct TwoDirect
{
    int bar(char);
    double bar() const;
};
struct TwoInherited : public OneInt, public OneDouble
{
    using OneInt::bar; // Required to avoid ambiguity
    using OneDouble::bar; // Required to avoid ambiguity
};
struct OneInheritedOneDirect : public OneInt
{
    using OneInt::bar; // Required to avoid hiding
    double bar() const;
};
struct OneInheritedOneDirect2 : public OneDouble
{
    using OneDouble::bar; // Required to avoid hiding
    int bar(char);
};

////////////////////////////////////
// Tests:
static_assert(HasIntBar<None> == false);
static_assert(HasIntBar<OneInt> == true);
static_assert(HasIntBar<OneIntInherited> == true);
static_assert(HasIntBar<OneDouble> == false);
static_assert(HasIntBar<OneDoubleInherited> == false);
static_assert(HasIntBar<TwoDirect> == true);
static_assert(HasIntBar<TwoInherited> == true);
static_assert(HasIntBar<OneInheritedOneDirect> == true);
static_assert(HasIntBar<OneInheritedOneDirect2> == true);

static_assert(HasAnyBar<None> == false);
static_assert(HasAnyBar<OneInt> == true);
static_assert(HasAnyBar<OneIntInherited> == true);
static_assert(HasAnyBar<OneDouble> == true);
static_assert(HasAnyBar<OneDoubleInherited> == true);
static_assert(HasAnyBar<TwoDirect> == true); // FAILS!
static_assert(HasAnyBar<TwoInherited> == true); // FAILS!
static_assert(HasAnyBar<OneInheritedOneDirect> == true); // FAILS!
static_assert(HasAnyBar<OneInheritedOneDirect2> == true); // FAILS!
Run Code Online (Sandbox Code Playgroud)

Art*_*yer 5

如果在多个碱基中发现名称查找不明确,您可以利用该名称查找。T首先创建一个从您的类型和某个具有单个成员的虚拟类型派生的结构bar,然后检查该派生类型是否可以找到bar。如果可以,则它并不含糊,因为它是在 导出 中找到的,而不是在 中T,因此T没有栏。否则T有一个bar

struct dummy_bar { int bar; };

template<typename T, bool IsClass = std::is_class<T>::value>
struct HasAnyBar_t {
    static_assert(!std::is_final<T>::value, "Cannot check HasAnyBar<T> on final classes for implementation reasons");

    template<typename U>
    static auto check(int) -> decltype(U::bar, void(), std::false_type{});
    template<typename U>
    static std::true_type check(long);  // Above was ambiguous, so bar did exist in T

    struct derived : T, dummy_bar {};

    static constexpr bool value = decltype(check<derived>(0))::value;
};

template<typename T>
struct HasAnyBar_t<T, false> : std::false_type {};

template<typename T>
constexpr bool HasAnyBar = HasAnyBar_t<T>::value;
Run Code Online (Sandbox Code Playgroud)

这会检查任何具有名称的内容bar:具有模板、静态数据成员、非静态数据成员和成员类型的函数重载集。