使用void_t
编写一个模板可以很容易地检测出某个类型中特定成员的存在:
#include <type_traits>
// This comes from cppreference
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
// primary template handles types that have no ::aMember
template< class T, class = void_t<> >
struct has_aMember : std::false_type { };
// specialization recognizes types that do have a ::aMember
template< class T >
struct has_aMember<T, void_t<decltype( T::aMember )>> : std::true_type { };
现在,如果我想检测是否存在其他成员,我必须复制粘贴检测器模板,然后将aMember
更改为otherMember
:
template< class T, class = void_t<> >
struct has_otherMember : std::false_type { };
template< class T >
struct has_otherMember<T, void_t<decltype( T::otherMember )>> : std::true_type { };
我想避免这种复制粘贴,并将成员名称作为参数传递给更通用的检测模板版本:
template< class T, class member, class = void_t<> >
struct has_arbitrary_member : std::false_type { };
template< class T, class member >
struct has_arbitrary_member<T, void_t<decltype( T::member )>> : std::true_type { };
这样我就可以通过将类型和成员名称作为模板参数传递来使用此has_arbitrary_member
:
std::cout << has_arbitrary_member<MyType, aMember>();
std::cout << has_arbitrary_member<MyType, otherMember>();
但是,根据我上面草拟的定义,这将无法编译。有没有其他方法来实现这样的功能?
答案 0 :(得分:8)
除非您使用宏修改令牌流,否则您通常无法在aMember
上无法禁止名称查找。因此,除非您当前在 SFINAE内部,否则您永远无法使用不在范围内的名称,这将抑制错误。
这意味着你真的必须为每个名字写一个单独的模板。
这是使用宏并不是一个坏主意的情况之一,尽管这取决于您是否确实需要检查特定名称的成员,或者是否有更好的方法来实现您的目标。
答案 1 :(得分:3)
不,您不能将成员名称作为模板参数传递,而不提及成员名称来自哪个类/结构。
如果该类/结构没有该成员名称,则代码有错误。
现在,void_t< decltype( &MyType::some_member ) >
是void
或替换失败,具体取决于some_member
是否存在。通常,这解决了SFINAE问题,你不需要编译时bool。
如果希望代码在某些类型不的情况下运行,可以通过仔细选择重载顺序或模板特化等来执行此操作。
它没有has_arbitrary_member<MyType, some_member>
那么强大。
template<class T>
using some_member_type = decltype( std::declval<T>().some_member );
是一个简单的特征,您可以使用std::experimental::is_detected:
is_detected_v< some_member_type, T >
或
template<class T>
constexpr bool has_some_member_v = is_detected_v< some_member_type, T >;
的行为类似于has_arbitrary_member<MyType, some_member>
。我觉得这在实践中是最好的。