C++17如何测试class有一个成员变量?
C++17 how to test class has a member variable?
我正沿着这条路走
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;
}
但调用 f<S>()
失败,因为
std::is_same_v<decltype(S::extra), int> == 0
和f<V>()
不编译
观察到decltype(T::extra)
(当T
是S
)是int const
(是constexpr
所以也是const
),不是int
。这解释了为什么 f<S>()
失败。
我想有很多方法可以测试 class 是否有成员变量。
一个可能的解决方案是开发一些东西作为
void type_extra (...);
template <typename T>
auto type_extra (T t) -> decltype(t.extra);
template <typename T>
using type_extra_t = decltype(type_extra(std::declval<T>()));
现在可以这样写f()
template <typename T>
void f()
{
if ( not std::is_same_v<type_extra_t<T>, void> )
{
std::cout<< "extra exists" <<std::endl;
if ( std::is_same_v<type_extra_t<T>, int> )
std::cout<< "(and is int)" << std::endl;
}
}
注意现在 type_extra_t<S>
是 int
,而不是 int const
;这样(从函数的 returned 类型获取类型)松散了变量的常量性。
如果你想保持常量,你可以return一个函数的引用(所以它return一个int const &
在S
的情况下)
template <typename T>
auto type_extra (T t) -> decltype(t.extra) &;
并从 using 中删除引用(因此,在 S
的情况下,int const &
变为 int const
)
template <typename T>
using type_extra_t
= std::remove_reference_t<decltype(type_extra(std::declval<T>()))>;
如果您被困在 c++17,您可以添加一些基础设施来使这样的检测更容易。
检测成语
最 reusable/consistent 检测此类功能的方法是通过模板中的 Detection idiom, which leverages SFINAE through std::void_t
。
这可以从 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')
}
如果对特定类型的等效转换很重要,例如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
}
概念(仅限 C++20+)
如果您可以访问 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')
}
我正沿着这条路走
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;
}
但调用 f<S>()
失败,因为
std::is_same_v<decltype(S::extra), int> == 0
和f<V>()
不编译
观察到decltype(T::extra)
(当T
是S
)是int const
(是constexpr
所以也是const
),不是int
。这解释了为什么 f<S>()
失败。
我想有很多方法可以测试 class 是否有成员变量。
一个可能的解决方案是开发一些东西作为
void type_extra (...);
template <typename T>
auto type_extra (T t) -> decltype(t.extra);
template <typename T>
using type_extra_t = decltype(type_extra(std::declval<T>()));
现在可以这样写f()
template <typename T>
void f()
{
if ( not std::is_same_v<type_extra_t<T>, void> )
{
std::cout<< "extra exists" <<std::endl;
if ( std::is_same_v<type_extra_t<T>, int> )
std::cout<< "(and is int)" << std::endl;
}
}
注意现在 type_extra_t<S>
是 int
,而不是 int const
;这样(从函数的 returned 类型获取类型)松散了变量的常量性。
如果你想保持常量,你可以return一个函数的引用(所以它return一个int const &
在S
的情况下)
template <typename T>
auto type_extra (T t) -> decltype(t.extra) &;
并从 using 中删除引用(因此,在 S
的情况下,int const &
变为 int const
)
template <typename T>
using type_extra_t
= std::remove_reference_t<decltype(type_extra(std::declval<T>()))>;
如果您被困在 c++17,您可以添加一些基础设施来使这样的检测更容易。
检测成语
最 reusable/consistent 检测此类功能的方法是通过模板中的 Detection idiom, which leverages SFINAE through std::void_t
。
这可以从 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')
}
如果对特定类型的等效转换很重要,例如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
}
概念(仅限 C++20+)
如果您可以访问 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')
}