如果您被困在c++17,您可以添加一些基础设施来使此类检测更容易。
检测成语
检测此类特征的最可重用/一致的方法是通过Detection idiom,它通过模板中的std::void_t利用SFINAE。
这可以从 cppreference 的std::experimental::is_detected 的页面逐字获取。这有效地为 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;
注意:上述基础设施可重复用于任何检测。它是 C++17 中非常有用的可复用工具。
使用is_detected,您只需要一个检测器,它只是一个模板别名,计算结果为decltype 表达式,表示可能存在或不存在的事物。
因此,在您的情况下,要有条件地检测 T::extra 的存在,您可以使用简单的检测器来执行此操作,例如:
template <typename T>
using detect_extra = decltype(T::extra);
现在把它们放在一起,你可以使用这个检测器有条件地切换分支:
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')
}
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>;
这允许支票改为:
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
}
Live Example
概念(仅限 C++20+)
如果您可以访问c++20 及其他权限,concepts 会更简单——因为您可以简单地使用 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')
}
Live Example