C++17如何测试类有成员变量?

MMM*_*MMM 4 c++ type-traits c++17

我正沿着路线走

struct S {
  static constexpr int extra=5;
};

struct V {
};


template <typename T>
void f()
{  
  if (std::is_same_v<decltype(T::extra), int>)
    std::cout<< "extra exists" <<std::endl;
}
Run Code Online (Sandbox Code Playgroud)

但调用f<S>()失败为

std::is_same_v<decltype(S::extra), int> == 0
Run Code Online (Sandbox Code Playgroud)

并且f<V>()不编译

Hum*_*ler 6

如果您陷入困境,您可以添加一些基础设施以使此类检测变得更加容易。

检测惯用语

检测此类功能的最可重用/一致的方法是通过检测惯用法std::void_t,它在模板中利用 SFINAE 。

std::experimental::is_detected这可以从cppreference 的页面逐字获取。这有效地为 C++17 提供了以类似于 C++20 概念的方式检测功能的能力;并且该基础设施可以轻松地重复用于任何检测。

您需要的基础知识是:

#include <type_traits>

namespace detail {
template <class Default, class AlwaysVoid,
          template<class...> class Op, class... Args>
struct detector {
  using value_t = std::false_type;
  using type = Default;
};
 
template <class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
  using value_t = std::true_type;
  using type = Op<Args...>;
};
 
} // namespace detail
 
struct nonesuch{};

template <template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
Run Code Online (Sandbox Code Playgroud)

注意:上述基础设施可以重复用于任何检测。它是 C++17 中非常有用的可重用工具。

使用is_detected,您所需要的只是一个detector,它只是一个模板别名,其计算结果为decltype可能存在或可能不存在的事物的表达式。

因此,在您的情况下,要有条件地检测 的存在T::extra,您可以使用简单的检测器来执行此操作,例如:

template <typename T>
using detect_extra = decltype(T::extra);
Run Code Online (Sandbox Code Playgroud)

现在将它们放在一起,您可以使用此检测器有条件地切换分支:

if constexpr (is_detected<detect_extra,T>::value) {
    // Only do code if 'T' has 'T::extra' (e.g. 'S')
} else {
    // Only do code if 'T' does not have 'T::extra' (e.g. 'V')
}
Run Code Online (Sandbox Code Playgroud)

Live Example


如果到特定类型的等效转换很重要,例如extra需要转换为int,您还可以使用is_detected_convertible 并使用检测器来检查结果是否可以转换为所需类型。再次使用相同的 cppreference 页面,您可以定义is_detected_convertible为:

template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;

template <class To, template<class...> class Op, class... Args>
using is_detected_convertible = std::is_convertible<detected_t<Op, Args...>, To>;
Run Code Online (Sandbox Code Playgroud)

这允许检查改为:

if constexpr (is_detected_convertible<int, detect_extra, T>::value) {
    // Only do code if 'T' has 'T::extra' convertible to int (e.g. 'S')
} else {
    // Only do code if 'T' does not have 'T::extra', or is not int
}
Run Code Online (Sandbox Code Playgroud)

Live Example

概念(仅限 C++20+)

如果您可以访问及更高版本,concept则会使这变得更简单 - 因为您可以简单地使用concept+requires子句,例如:

#include <concepts> // std::same_as

template <typename T>
concept HasExtra = requires(T) {
  {T::extra} -> std::same_as<int>;
};

if constexpr (HasExtra<T>) {
    // Only do code if 'T' has 'T::extra' and is 'int' (e.g. 'S')
} else {
    // Only do code if 'T' does not have 'T::extra' (e.g. 'V')
}
Run Code Online (Sandbox Code Playgroud)

Live Example