C++ 如何拥有具有类型和大小的可变模板?

C++ 如何拥有具有类型和大小的可变模板?,c++,c++11,templates,C++,C++11,Templates,只是为了好玩,我尝试重载一个结构,一个用于std::array,一个用于std::vector,还有一个std::unordered_映射。因此,我做了以下工作: template<typename... T> struct Cont; template<typename T, std::size_t SIZE> struct Cont<T,SIZE> { Cont(std::string n) : name(n){}

只是为了好玩,我尝试重载一个结构,一个用于std::array,一个用于std::vector,还有一个std::unordered_映射。因此,我做了以下工作:

  template<typename... T>
  struct Cont; 

  template<typename T, std::size_t SIZE>
  struct Cont<T,SIZE>
  {
    Cont(std::string n) : name(n){}

    std::string name;
    std::array<T,SIZE> array;
  };

  template<typename T>
  struct Cont<T>
  {
    Cont(std::string n) : name(n){}

    std::string name;
    std::vector<T> vector;
  };

  template<typename T, typename U>
  struct Cont<T,U>
  {
    Cont(std::string n) : name(n){}

    std::string name;
    std::unordered_map<T,U> unordered_map;
  };
这也不起作用,因为我当时正在重新定义,而不是像我最初认为的那样超载。我也意识到,我可以做到:

template<std::size_t SIZE, typename... T>
struct Cont;
然而,在我宣布我希望能够做到的事情时,我正在尽可能地保持这一点:

int main()
{
  Cont<int,5> myArray("myArray");
  myArray.array = {1,2,3,4,5};

  Cont<int> myVector("myVector");
  myVector.vector = {1,2,3,4};

  Cont<int,int> myMap("myMap");
  myMap.unordered_map[0] = 2;
}

有没有一种方法可以重载模板?我假设这是否定的,或者以一种表示我是typename。。。T或类型名T,std::size\u T size

我不知道如何得到你想要的东西。也许一个更聪明的用户有一个精确的解决方案。但另一种选择可能是为模板提供一个类型参数包,并为数值模板参数使用std::integral_常量。然后你可以针对每种情况进行专门研究。例如:

#include <array>
#include <type_traits>
#include <unordered_map>
#include <vector>

template<class...>
struct Cont;

template<class T, class U, U S>
struct Cont<T, std::integral_constant<U, S>>
{
    Cont(const char *) {};
    std::array<T, S> array;
};

template<class T>
struct Cont<T>
{
    Cont(const char *) {};
    std::vector<T> vector;
};

template<class T>
struct Cont<T, T>
{
    Cont(const char *) {};
    std::unordered_map<T, T> unordered_map;
};

int main()
{
  Cont<int,std::integral_constant<int, 5>> myArray("myArray");
  myArray.array = {1,2,3,4,5};

  Cont<int> myVector("myVector");
  myVector.vector = {1,2,3,4};

  Cont<int,int> myMap("myMap");
  myMap.unordered_map[0] = 2;
}
您可以将其设置为Cont,然后创建一个模板struct Cont;专业化,但这打破了其他类型的模式

另一件事是,除了通过将5封装在std::integral_常量中而将其转换为一个类型之外,还可以创建一对重载的帮助函数来自动执行此操作:

template<typename... T>
Cont<T...> make_cont(std::string name) {
    return { std::move(name) };
}

template<typename T, std::size_t SIZE>
Cont<T, std::integral_constant<std::size_t, SIZE>> make_cont(std::string name) {
    return { std::move(name) };
}

int main() {
    auto myArray = make_cont<int, 5>("myArray");
    myArray.array = {1,2,3,4,5};

    auto myVector = make_cont<int>("myVector");
    myVector.vector = {1,2,3,4};

    auto myMap = make_cont<int, int>("myMap");
    myMap.unordered_map[0] = 2;
}

如果您像这样声明数组版本

template <typename T, std::size_t SIZE>
struct Cont<std::array<T, SIZE>>
{
  Cont(std::string n) : name(n) {}

  std::string name;
  std::array<T, SIZE> array;
};
然后大体上你可以这样使用它:

int main() {
  Cont<std::array<int, 5>> myArray("myArray");
  myArray.array = {1, 2, 3, 4, 5};

  Cont<int> myVector("myVector");
  myVector.vector = {1, 2, 3, 4};

  Cont<int, int> myMap("myMap");
  myMap.unordered_map[0] = 2;

  std::cout << "myArray " << myArray.array[0] << std::endl;
  // myArray 1

  std::cout << "myVector " << myVector.vector[0] << std::endl;
  // myVector 1

  std::cout << "myMap " << myMap.unordered_map[0] << std::endl;
  // myMap 2
}
我认为这是你正在寻找的功能,它是干净和可读的


这是因为std::array指定了模板所需的类型,并且它包含定义std::array所需的std::SIZE\T信息。

最简单的方法是使用SIZE参数和该参数的默认值声明模板。如果size是对象数,则默认值为size=1。或者专门针对size=1,仔细查看代码。这并不漂亮,但确实有效!当我看到像这样的东西时,作为一名程序员,我需要多长时间来思考std::integral_常量?@Sailanarmo我不确定这是否应该是思考std::integral_常量的反射。只使用std::size\u t或int更好。但我发现,当类型参数和值参数混合使用时,通常可以解决问题。到目前为止,我的建议是尽可能使用数字模板参数,因为它更容易阅读和理解,更不用说更易于使用。可能有一个自动模板的解决方案,但我没有太多机会应用它们。我从来没有听说过自动模板。我要去找他们!啊,我希望我能接受你的两个答案!您添加的特性使它看起来更干净,François的回答很好;是否?@Sailanarmo它使用一个参数从返回类型创建一个Cont:std::movename。因为函数的名称不是const std::string&,而是std::string,所以它会生成一个副本。它将移动临时副本,而不是制作另一个副本。明白了!如果它是const std::string&,该怎么办?你能把名字还给我吗?这似乎不正确,因为名称是一个字符串。@Sailanarmo如果它是make_cont const std::string&name,则不能从中移动。您必须返回{name}这些大括号调用构造函数,这将再次生成name的副本
int main() {
  Cont<std::array<int, 5>> myArray("myArray");
  myArray.array = {1, 2, 3, 4, 5};

  Cont<int> myVector("myVector");
  myVector.vector = {1, 2, 3, 4};

  Cont<int, int> myMap("myMap");
  myMap.unordered_map[0] = 2;

  std::cout << "myArray " << myArray.array[0] << std::endl;
  // myArray 1

  std::cout << "myVector " << myVector.vector[0] << std::endl;
  // myVector 1

  std::cout << "myMap " << myMap.unordered_map[0] << std::endl;
  // myMap 2
}