检查类T是否具有成员类型成员void_t

时间:2016-01-20 15:19:30

标签: c++ templates c++11 c++14 sfinae

所以这里是代码:

template<typename, typename, typename = void>
struct has_member_type : false_type {};

template<typename T, typename Member>
struct has_member_type<T, Member, void_t<typename T::Member>> : true_type {};

struct foo { using bar = int; };

int main()
{
    std::cout << has_member_type<foo, typename foo::bar>::value;
}

我试图检查foo是否有bar类型的成员。如果实现没有指定类型成员的名称,它可以正常工作,但是这样名称被硬编码到实现中,这对我来说并不起作用。

说重复的问题并没有接近回答我的问题。正如我在上面的段落中所解释的那样,当类型被硬编码到实现中时它很好,但是当我从外部指定类型时我无法工作(这是特定的问题) )。代码编译得很好,但会产生错误的结果。

2 个答案:

答案 0 :(得分:6)

您的代码无效,因为typename foo::bar只是直接解析为int,而不是传递一些可用于SFINAE的构造。

一种可能的解决方案是创建一个别名模板,它会告诉您T::bar的类型,然后将其传递给您的检查器。这就是std::experimental::is_detected的作用。这是一个简化的版本,它接近你已经*:

template<typename, template <typename> class, typename = void>
struct is_detected : false_type {};

template<typename T, template <typename> class Op>
struct is_detected<T, Op, void_t<Op<T>>> : true_type {};

然后编写要检测的别名模板:

template <typename T> using bar_t = typename T::bar;

用法如下:

is_detected <foo, bar_t>::value

*:我保持模板参数的方式与示例代码相同,这样您就可以轻松比较。翻转它们以便在更通用的上下文中使操作符参数变量更好。它还可以让您在可用时更容易迁移到std::experimental::is_detected

答案 1 :(得分:2)

一旦N4487被接受,你就可以在常量表达式中使用泛型lambda:

template <typename T, typename F>
constexpr auto test(F f) -> decltype(f(std::declval<T&>()), true) {return true;}

template <typename, typename... F>
constexpr bool test(F...) {return false;}

#define HAS_DATA(Name, ...) (test<__VA_ARGS__>([] (auto& t) -> decltype(&std::decay_t<decltype(t)>::Name) {}))
#define HAS_TYPE(Name, ...) (test<__VA_ARGS__>([] (auto& t) -> typename std::decay_t<decltype(t)>::Name {}))

Democout