递归可变参数模板的基本案例专业化

Base case specialization for recursive variadic template

我的目标是定义一个 Recursive class,以 int N 和一种或多种类型 T, ...Ts 为模板,其行为应该类似于 std::pair

在尝试根据上述要求写下 class 时,我想出了这个无效代码(我还定义了一些必要的,因为它们有很大帮助,别名Recursive 的两个实例化),我不知道我是否错误地设计了我上面描述的内容(或者它是否是一个错误的描述!),或者我是否滥用了语言语法。

#include <array>
#include <boost/hana/fwd/optional.hpp>
#include <boost/hana/optional.hpp>
#include <string>
#include <utility>
#include <vector>

template <int N, typename T1, typename T2, typename ...Ts>
struct Recursive
    : std::pair<std::array<T1, N>, 
                boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>> {};

template <int N, typename T>
struct Recursive<N, T> : std::array<T, N> {};

template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main() {
    using boost::hana::nothing;
    Recursive2<int> x(std::make_pair(std::array<int, 2>{0,0}, nothing));
}

我将添加一些我目前已完成的故障排除。在下文中,模板专业化似乎工作得很好。

#include <iostream>

template <int N, typename T, typename ...Ts>
struct Recursive {
    void operator()(){ std::cout << "general\n"; }
};

template <int N, typename T>
struct Recursive<N, T> {
    void operator()(){ std::cout << "specialized\n"; }
};

template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main() {
    Recursive2<int>{}();
    Recursive2<int>{}();
    Recursive2<int,int>{}();
}

你的错误是,首先,你声明了 Recursive 接收至少一个整数和 两个 或更多类型,然后,你声明了一个部分特化接收一个整数和恰好 一个 类型。

错误,因为当主模板声明接收两种或更多类型时,专业化不能只接收一种类型。

可以是 counter-intuitive 但解决方案可以声明 Recursive 只接收 一个 类型或更多(这成为递归的基本情况)以及接收 两种 类型或更多

的专业化
template <int N, typename T1, typename...>
struct Recursive : std::array<T1, N>
 { };

template <int N, typename T1, typename T2, typename ...Ts>
struct Recursive<N, T1, T2, Ts...>
   : std::pair<std::array<T1, N>,
               boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>>
 { };

以下稍作修改(std::size_t 而不是 int 大小;std::optional 而不是 boost::hana::optional)但完全编译示例

#include <array>
#include <optional>
#include <string>
#include <utility>
#include <vector>

template <std::size_t N, typename T1, typename...>
struct Recursive : std::array<T1, N>
 { };

template <std::size_t N, typename T1, typename T2, typename ...Ts>
struct Recursive<N, T1, T2, Ts...>
   : std::pair<std::array<T1, N>,
               std::optional<std::vector<Recursive<N, T2, Ts...>>>>
 { };

template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main ()
 {
    Recursive2<int> x{std::array<int, 2u>{0,0}};
    Recursive3<int, long> y{{std::array<int, 3u>{0,0,0}, {}}};
 }

您有几个问题:

  • 您的专业化与您的主要模板不匹配

    template <int N, typename T1, typename T2, typename ...Ts> struct Recursive; 需要至少 3 个参数。我认为它应该是一个专业,主要模板应该是:

    template <int N, typename T1, typename ...Ts>
    struct Recursive;
    
  • template <int N, typename T> struct Recursive<N, T> 的行为不像 std::pair (正如您陈述的要求,否则您的用法是错误的),您可能想要这样的东西:

    template <int N, typename T>
    struct Recursive<N, T> : std::pair<std::array<T, N>, decltype(boost::hana::nothing)>
    
  • 你需要“转发”基础的构造函数class,(组合而不是继承也可能是一个选项,或者定义要使用的类型的特征)或改变方式构造对象。

结果是:

template <int N, typename T1, typename ...Ts>
struct Recursive;

template <int N, typename T1, typename T2, typename ...Ts>
struct Recursive<N, T1, T2, Ts...>
    : std::pair<std::array<T1, N>,
                boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>
                            >
{
    using std::pair<
        std::array<T1, N>,
        boost::hana::optional<std::vector<Recursive<N, T2, Ts...>>>>::pair;
};

template <int N, typename T>
struct Recursive<N, T>
    : std::pair<std::array<T, N>, decltype(boost::hana::nothing)>
{
    using std::pair<std::array<T, N>, decltype(boost::hana::nothing)>::pair;
};

template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main() {
    using boost::hana::nothing;
    Recursive2<int> x(std::make_pair(std::array<int,2>{0,0}, nothing));
}

Demo

我正在添加我自己的答案,因为我确实找到了解决方案(在收到两个答案之前;使用 std::optional 是我根据其中一个答案所做的后期更改)。但是,在我的解决方案中,我 不得不 声明和定义通用模板和专用模板的构造函数,这让我认为它不如其他答案那么好。但是为什么不发呢?

#include <cassert>
#include <iostream>
#include <array>
#include <optional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

template <int N, typename T, typename ...Ts>
struct Recursive : std::pair<std::array<T, N>,
                             std::optional<std::vector<Recursive<N, Ts...>>>
                   > {
    template<typename ...Args>
    Recursive(Args&& ...args) : std::pair<std::array<T, N>,
                                std::optional<std::vector<Recursive<N, Ts...>>>
                  >(args...) {}
};

template <int N, typename T>
struct Recursive<N, T> : std::array<T, N> {
    template<typename ...Args>
    Recursive(Args&& ...x) : std::array<T, N>(x...) {}
};


template<typename ...T>
using Recursive2 = Recursive<2u, T...>;

template<typename ...T>
using Recursive3 = Recursive<3u, T...>;

int main() {
    std::array<std::string, 2> twoStrings{"hello","Hello"};
    std::array<char, 2> twoChars{'h', 'H'};

    Recursive2<std::string> s{twoStrings};
    assert(s == twoStrings);

    std::vector<Recursive2<char>> vecOfTwoChars{twoChars, twoChars, twoChars};

    Recursive2<std::string, char> sc{twoStrings, vecOfTwoChars};
    assert(sc.first == twoStrings);
    assert(sc.second->size() == 3);
    assert(sc.second == vecOfTwoChars);
    assert(sc.second.value()[0] == twoChars);

}