【问题标题】:Checking if class T has member type Member with void_t检查类 T 是否具有成员类型 Member 和 void_t
【发布时间】:2016-04-26 12:33:37
【问题描述】:

代码如下:

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 类型成员。如果实现没有指定类型成员的名称,它可以正常工作,但是这样名称被硬编码到实现中,这对我不起作用。

说是重复的问题并不能回答我的问题。正如我在上面的段落中所解释的,当类型被硬编码到实现中时很好,但是当我从外部指定类型时我无法让它工作(这是具体问题)。代码编译正常,但产生错误的结果。

【问题讨论】:

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


    【解决方案1】:

    您的代码不起作用,因为 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

    【讨论】:

    • 这样,他需要一个辅助模板bar_t,对吧?
    • @Columbo 是的,bar_t 在我的回答中。
    • 关键是他正试图避免为每个名称添加任何额外的实体。 bar_t 是他不希望硬编码名称的实现的一部分。
    • @Columbo 我将“硬编码到实现中”放入has_member_type 特征中。
    • 最好模仿std::experimental::is_detected&lt;Op, Args&gt; 而不是is_detected&lt;Args, Op&gt;,就像你在这里写的那样,当&lt;experimental/type_traits&gt; 标头在OP 的系统上可用时,可以更轻松地插入它
    【解决方案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

    【讨论】:

    • 很好,但应该返回 integral_constant
    • 因此它可以传递给函数的参数,并且该值可以用于重载(例如标签调度)和static_assert。此外,在某些情况下它可以更清洁。例如,将其作为成员函数的模板参数传入需要.template 来消除歧义,而将其作为函数参数传递则不需要。
    • @Paul 如果使用标签调度,则将结果值封装在bool_constant 中。我不明白为什么名为“HAS_...”的宏的应用程序应该返回 bool 以外的其他内容。
    • 但是为什么要求用户使用bool_constant呢?此外,类型特征已经以这种方式工作。那么为什么要通过返回原始bool 来降低HAS_* 的能力呢?没有优势。
    • @Paul HAS_ 的能力丝毫不逊色。而integral_constant 是 C++11 的残余。使用概念和变量模板,惯用语义会发生变化。你会写requires HAS_TYPE(T, bar)。如果你想改写requires HAS_TYPE(T, bar)::value,那是你的选择。但我不会强加给未来的 C++17 读者。
    猜你喜欢
    • 1970-01-01
    • 2014-12-09
    • 1970-01-01
    • 1970-01-01
    • 2011-01-08
    • 1970-01-01
    • 2020-06-11
    • 2017-06-15
    • 1970-01-01
    相关资源
    最近更新 更多