【问题标题】:Checking if variadic template parameters are unique using fold expressions使用折叠表达式检查可变参数模板参数是否唯一
【发布时间】:2017-11-27 12:50:13
【问题描述】:

给定一个可变参数模板参数包,我想使用inline constexpr boolfold expressions 检查给定给它的所有类型是否都是唯一的。我尝试这样的事情:

template<class... T>
inline static constexpr bool is_unique = (... && (!is_one_of<T, ...>));

is_one_of 是一个可以正常工作的类似布尔值。 但是无论我在 is_one_of 中输入了什么,这条线都不会编译。这甚至可以使用折叠表达式来完成,还是我需要为此使用常规结构?

【问题讨论】:

    标签: c++ templates c++17 variadic-templates fold-expression


    【解决方案1】:

    您的方法实际上不起作用,因为需要使用类型 T 和所有其余类型不包括 T 来调用 is_one_of。没有办法在单个参数包上使用 fold 表达式 来表达这一点。我建议改用专业化:

    template <typename...>
    inline constexpr auto is_unique = std::true_type{};
    
    template <typename T, typename... Rest>
    inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
        (!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
    >{};   
    

    用法:

    static_assert(is_unique<>);
    static_assert(is_unique<int>);
    static_assert(is_unique<int, float, double>);
    static_assert(!is_unique<int, float, double, int>);
    

    live example on wandbox.org


    (感谢Barry 使用折叠表达式的简化。)

    【讨论】:

    • 你是对的。我非常信任我的 YouCompleteMe 配置,尽管它甚至没有深入了解所有 c++17 功能,但它却出人意料地给了我诊断。漠视。我必须弄清楚它为什么会起作用,尽管因为我怀疑给定的案例会失败,因为我是从大脑编译它的。知道了;我的大脑编译器没有将T1, Rest... 视为“只是尾巴”,它当然是。 +1
    • 可以用(!is_same_v&lt;T0, Rest&gt; &amp;&amp; ...) &amp;&amp; is_unique&lt;Rest...&gt;的正文简化为T0, Rest...吗?
    • 虽然另一个答案提供了一种使用 fold 表达式和 constexpr bool 来实现这一点的方法,但它带有很多警告,并且仍然需要我想要不使用的其他结构,所以你的答案“你做不到”更合适。谢谢!
    【解决方案2】:

    -- 编辑--

    谷歌搜索我发现了一个interesting solution,它给了我避免递归和避免大量警告的灵感

    所以你可以定义一个类型的包装器

    template <typename>
    struct wrapT
     { };
    

    以及继承自类型包装器的类型和整数包装器

    template <typename T, std::size_t>
    struct wrapTI : public wrapT<T>
     { };
    

    接下来你可以定义一个foo递归地继承自wrapTI

    template <typename T,
              typename = std::make_index_sequence<std::tuple_size<T>::value>>
    struct foo;
    
    template <typename ... Ts, std::size_t ... Is>
    struct foo<std::tuple<Ts...>, std::index_sequence<Is...>>
       : public wrapTI<Ts, Is>...
     { };
    

    现在is_unique 可以是这样的

    template <typename ... Ts>
    static constexpr bool isUnique
       = ( ... && std::is_convertible<foo<std::tuple<Ts...>>, wrapT<Ts>>::value );
    

    关键是foo&lt;Ts...&gt;只有在foo&lt;Ts...&gt;wrapT&lt;T&gt;继承一次(且仅一次)时才能转换为wrapT&lt;T&gt;,即如果T存在一次(且仅一次)时间)在Ts...

    以下是完整的编译示例

    #include <tuple>
    #include <type_traits>
    
    template <typename>
    struct wrapT
     { };
    
    template <typename T, std::size_t>
    struct wrapTI : public wrapT<T>
     { };
    
    template <typename T,
              typename = std::make_index_sequence<std::tuple_size<T>::value>>
    struct foo;
    
    template <typename ... Ts, std::size_t ... Is>
    struct foo<std::tuple<Ts...>, std::index_sequence<Is...>>
       : public wrapTI<Ts, Is>...
     { };
    
    template <typename ... Ts>
    static constexpr bool isUnique
       = ( ... && std::is_convertible<foo<std::tuple<Ts...>>, wrapT<Ts>>::value );
    
    int main ()
     {
       static_assert( true == isUnique<int, long, long long> );
       static_assert( false == isUnique<int, long, long long, int> );
     }
    

    【讨论】:

    • 虽然您的答案提供了一种使用 fold 表达式和 constexpr bool 实现此目的的方法,但它带有很多警告,并且仍然需要我想要不使用的其他结构,所以另一个答案“你做不到”更合适。但是谢谢,很高兴知道它可以做到!
    • @Joald - 我同意你的看法:Vittorio 的回答比我的好(而且我发现这些警告很烦人)
    • @Joald - 只是为了好玩...修改了答案,避免了递归并删除了警告:) - 但添加了另一个包装结构:(
    • 这是检查我正在寻找的唯一性的非常好的解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多