C++ 特化多模板参数包

标签 c++ variadic-templates template-specialization

我正在使用参数包,并得到了一些我无法解释的行为(根据我的理解,它应该是有效的)。

我有一个模板化结构,采用一种类型和使用两个参数包的特化 ( http://coliru.stacked-crooked.com/a/0cb6c6fef7b09e6f ):

    #include <type_traits>
    template<class, int> struct dummy{};
    template<class...> struct multiple{};

    template<class T, class = std::true_type>
    struct exactly_one_not_0 : std::false_type {};

    template<class...LTs, class...RTs, class T, int V>
    struct exactly_one_not_0
    <
        multiple
        <
            dummy<LTs, 0>...,
            dummy<T, V>,
            dummy<RTs, 0>...
        >,
        std::bool_constant<V>
    > : std::true_type
    {};

    template<class T>
    constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;

    using d0 = dummy<int,0>;
    using d1 = dummy<int,1>;

    static_assert(exactly_one_not_0_v<multiple<d1>>);
    static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
    static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
    static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);

    static_assert(!exactly_one_not_0_v<multiple<>>);
    static_assert(!exactly_one_not_0_v<multiple<d0>>);
    static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
    static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
    static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
    static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
    static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);

前四个断言失败。为什么?特化应该能够捕获这些案例。

相比之下,以下代码按预期工作( http://coliru.stacked-crooked.com/a/871ee1cc28f5ddc9 ):

    #include <iostream>
    template<class T>
    struct multi_pack
    {
          static void f() {std::cout << "base case\n";}
    };

    template<class...Ts1, class...Ts2>
    struct multi_pack<std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>>
    {
        using T = std::pair<std::tuple<Ts1...>, std::tuple<Ts2...>>;
        static void f() {std::cout << "special case\n";}
    };

知道为什么第一个示例失败而第二个示例有效吗?

最佳答案

在第二种情况下,您要求编译器匹配不同类型内的包(有一对 Ts1 元组和 Ts2 元组) 。这绝对是明确的。

在第一个示例中,两个包都在同一参数包中使用,并由第三种类型分隔。我理解为什么您希望它能够工作,但显然编译器(gcc 和 clang)拒绝完全匹配带前缀的包。

在您的特定情况下,它实际上在某些情况下是不明确的:由于 V 是一个参数,它也可能是 0,这使得匹配 d0 序列变得不明确。无论如何,我尝试过即使使用常量 1 也不能解决问题。您必须自己删除前缀。

#include <type_traits>
template<class, int> struct dummy{};
template<class...> struct multiple{};

template<class T, class = void> // <-- was this supposed to be an enabler?
struct exactly_one_not_0 : std::false_type {};

template<class Ts, class...RTs, int V>
struct exactly_one_not_0
<
    multiple
    <
        dummy<Ts, V>,
        dummy<RTs, 0>...
    >,
    std::enable_if_t<V!=0>
> : std::bool_constant<V>
{};

template<class LT, class...RDummies>
struct exactly_one_not_0
<
    multiple
    <
        dummy<LT, 0>,
        RDummies...
    >
> : exactly_one_not_0<multiple<RDummies...>>
{};

template<class T>
constexpr bool exactly_one_not_0_v = exactly_one_not_0<T>::value;

using d0 = dummy<int,0>;
using d1 = dummy<int,1>;

static_assert(exactly_one_not_0_v<multiple<d1>>);
static_assert(exactly_one_not_0_v<multiple<d1,d0>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1>>);
static_assert(exactly_one_not_0_v<multiple<d0,d1,d0>>);

static_assert(!exactly_one_not_0_v<multiple<>>);
static_assert(!exactly_one_not_0_v<multiple<d0>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d0>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d0,d1,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d0,d1>>);
static_assert(!exactly_one_not_0_v<multiple<d1,d1,d0>>);

int main()
{
    return 0;
}

为了不实例化多个多个,您可能也想将其删除

template<class T>
struct exactly_one_not_0 : std::false_type {};

template<class Enable, class... Ts> // <-- was this supposed to be an enabler?
struct exactly_one_not_0_impl : std::false_type {};

template<class... Ts>
struct exactly_one_not_0<multiple<Ts...>>
    : exactly_one_not_0_impl<void, Ts...>
{};

template<class Ts, class...RTs, int V>
struct exactly_one_not_0_impl
<
    std::enable_if_t<V!=0>,
    dummy<Ts, V>,
    dummy<RTs, 0>...
> : std::bool_constant<V>
{};

template<class LT, class...RDummies>
struct exactly_one_not_0_impl
<
    void,
    dummy<LT, 0>,
    RDummies...
> : exactly_one_not_0_impl<void, RDummies...>
{};

关于C++ 特化多模板参数包,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51038449/

相关文章:

c++ - 优雅地切换一组函数的模板参数

采用继承类的 C++ 非类型模板参数

c++ - 类模板特化的运算符重载

c++ - 将转换构造函数添加到模板的特化中

c++ - 成对访问 map 中的值

c++ - GLFW 在 Visual Studio Community 中不工作

c++ - 将多个 std::collections 输出到 CSV/row 第一次迭代的函数

c++ - 这是编译器错误还是程序员错误?

c++ - 在 5d map C++ 中查找项目

c++ - 变量的不完整类型 - 虽然我认为我已经正确声明了前向?