创建具有给定大小的 n 维向量

Create n-dimensional vector with given sizes

所以,我想要的是创建给定类型的多维向量,其中第一个维度将具有函数调用等的第一个参数的大小,例如如果我这样做

std::size_t n = 5;
auto x = make_vector<int>(n + 1, n * 2, n * 3);

x 应该是 6x10x15 3d 数组(由零组成,因为我现在想默认构造)

我试过这个:

template <typename T>
std::vector<T> make_vector(std::size_t size) {
    return std::vector<T>(size);
}

template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes) -> std::vector<decltype(make_vector<T>(sizes...))> {
    auto inner = make_vector<T>(sizes...);
    return std::vector<decltype(inner)>(first, inner);
}

它似乎适用于 1 或 2 个参数,但对于 3 个参数失败并出现以下错误(clang++)

In file included from /Users/riad/ClionProjects/for-jhelper/output/run.cpp:1:
/Users/riad/ClionProjects/for-jhelper/tasks/TaskC.cpp:12:12: error: no matching function for call to 'make_vector'
                auto x = make_vector<int>(n + 1, n * 2, n * 3);
                         ^~~~~~~~~~~~~~~~
/Users/riad/ClionProjects/for-jhelper/tasks/../spcppl/make_vector.hpp:9:6: note: candidate template ignored: substitution failure [with T = int, Args = <unsigned long, unsigned long>]: call to function 'make_vector' that is neither visible in the template definition nor found by argument-dependent lookup
auto make_vector(std::size_t first, Args... sizes) -> std::vector<decltype(make_vector<T>(sizes...))> {
     ^                                                                     ~~~~~~~~~~~
/Users/riad/ClionProjects/for-jhelper/tasks/../spcppl/make_vector.hpp:4:16: note: candidate function template not viable: requires single argument 'size', but 3 arguments were provided
std::vector<T> make_vector(std::size_t size) {

如果我理解正确,问题是当编译器试图计算 make_vector 的 return 值时,它必须知道具有较少参数的向量的 return 值并且无法计算这样做。我该如何解决?

我设法通过单独计算类型来做到这一点,但尽管它很短,但似乎没有必要。

template <typename T, int n>
struct NDVector {
    typedef std::vector<typename NDVector<T, n - 1>::type> type;
};

template <typename T>
struct NDVector<T, 0> {
    typedef T type;
};

template <typename T>
std::vector<T> make_vector(std::size_t size) {
    return std::vector<T>(size);
}


template <typename T, typename... Args>
typename NDVector<T, sizeof...(Args) + 1>::type make_vector(std::size_t first, Args... sizes) {
    typedef typename NDVector<T, sizeof...(Args) + 1>::type Result;
    return Result(first, make_vector<T>(sizes...));
}

非常感谢更优雅的解决方案

创建一个命名空间来放置一些助手,命名为details

details中创建类型struct adl_helper{};

创建 make_vector 的实现,除了它的第一个参数始终是名为 Adl 的模板参数。这个模板参数从不命名,它的实例被传递给递归。

通过调用 details::make_vector<T>( details::adl_helper{}, blah ).

实现 make_vector( blah )
namespace details {
  struct adl_helper { };

  template <class T, class Adl>
  std::vector<T> make_vector(Adl, size_t size) {
    return std::vector<T>(size);
  }

  template <class T, class Adl, class... Args,
    class R_T=decltype(
      make_vector<T>(Adl{}, std::declval<Args>()...)
    ),
    class R=std::vector<R_T>
  >
  R make_vector(Adl, size_t first, Args... sizes) 
  {
    auto inner = make_vector<T>(Adl{}, std::forward<Args>(sizes)...);
    return R(first, inner);
  }
}


template <class T, class... Args,
  class R=decltype(
    details::make_vector<T>(details::adl_helper{}, std::declval<Args>()...)
  )
>
R make_vector(Args... args)
{
  return details::make_vector<T>(details::adl_helper{}, std::forward<Args>(args)...);
}

这里发生的事情是在两个上下文中评估模板函数签名中看似递归的调用。

首先,它在声明的地方进行计算。特别是,它在定义 之前被计算 。所以它本身并不"catch"。

其次,基于 ADL(参数相关查找)的传递在实例化时仅基于传递给函数的模板类型完成。

template<class Adl>adl_helper 类型意味着此参数依赖查找可以在实例化时看到 details::make_vector 函数本身。当它查找 return 类型时,它可以 看到 details::make_vector。等等

live example.

使用 class R= 别名等只是为了清理代码并减少一些不必要的重复。

在这种特殊情况下,构建 return 类型所需的努力比所有这些体操都容易。

我认为this solution is cleaner:

template<class T>struct tag{using type=T;};
template<class Tag>using type=typename Tag::type;

template<class T, size_t n>
struct n_dim_vec:tag< std::vector< type< n_dim_vec<T, n-1> > > > {};
template<class T>
struct n_dim_vec<T, 0>:tag<T>{};
template<class T, size_t n>
using n_dim_vec_t = type<n_dim_vec<T,n>>;

有趣的问题!您 运行 遇到的问题是,不合格的名称查找将在使用它的范围内查找(按一般性递增的顺序)。但是,从 [basic.scope.pdecl]:

The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its initializer (if any)

在这个函数中:

template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes) 
-> std::vector<decltype(make_vector<T>(sizes...))> {
    ...
}

"complete declarator" 包括尾随 return 类型。因此函数模板 make_vector<T, Args...>{ 之前不会在范围内。这就是为什么这不会编译 3 个以上的参数。

最简单的修复是,如果您可以访问 C++14,您根本不需要尾随 return 类型;

template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes)
{ /* exactly as before */ }

在 name 函数体内,您可以毫无问题地进行递归调用。

如果没有 C++14,您可以将其转发到一个 class 模板,其名称将在所有递归调用的范围内:

template <typename T, size_t N>
struct MakeVector
{
    template <typename... Args>
    static auto make_vector(std::size_t first, Args... sizes)
    -> std::vector<decltype(MakeVector<T, N-1>::make_vector(sizes...))>
    {
        auto inner = MakeVector<T, N-1>::make_vector(sizes...);
        return std::vector<decltype(inner)>(first, inner);        
    }
};

template <typename T>
struct MakeVector<T, 1>
{
    static std::vector<T> make_vector(std::size_t size) {
        return std::vector<T>(size);
    }
};

template <typename T, typename... Args>
auto make_vector(Args... args)
-> decltype(MakeVector<T, sizeof...(Args)>::make_vector(args...))
{
    return MakeVector<T, sizeof...(Args)>::make_vector(args...);
}

我发布此内容时并不知道其他答案。不要删除它,以防它对某人有用。当然,在启用 C++14 的情况下,解决方案非常简单。

使用[下面的代码](Demo@ideone你可以实现:

  size_t n = 5;
  auto x = make_vector<int>(n+1, n*2, n*3);

下面是注释最少的代码:

// Creating a multidimensional container (e.g. `vector`, `map`)
template<template<typename...> class Container,
         typename T,  
         size_t DIMENSION>
struct MultiDimensional
{
  using internal = MultiDimensional<Container, T, DIMENSION-1>;
  using type = Container<typename internal::type>;

  template<typename... Args>
  static
  type Generate (const size_t size, Args... sizes)
  {
    return type(size, internal::Generate(sizes...));
  }
};

// Last dimension overload
template<template<typename...> class Container,
         typename T>
struct MultiDimensional<Container, T, 1>  
{
  using internal = T;
  using type = Container<T>;

  static
  type Generate (const size_t size)
  {
    return type(size);
  }
};

// Wrapper for the specific requirement of creating a multidimensional `std::vector`
template<typename T,
         typename... Args>
auto make_vector(Args... sizes)
 -> typename MultiDimensional<std::vector, T, sizeof...(sizes)>::type
{
  return MultiDimensional<std::vector, T, sizeof...(sizes)>::Generate(sizes...);
}

最简单的解决方案是回到 C:

void foo(size_t n) {
    int (*threeDArray)[2*n][3*n] = malloc((n + 1)*sizeof(*threeDArray));

    //Do with your array whatever you like.
    //Here I just initialize it to zeros:
    for(size_t i = 0; i < n + 1; i++) {
        for(size_t j = 0; j < 2*n; j++) {
            for(size_t k = 0; k < 3*n; k++) {
                threeDArray[i][j][k] = 0;
            }
        }
    }

    free(threeDArray);
}

正如我所说,这在 C++ 中是不可能的:C++ 标准要求所有数组大小都是编译时常量。 C 在这方面更加灵活,自 C99 以来,任何地方都允许 运行 时间数组大小,甚至在 typedef 秒内。

所以,当我有一些代码必须认真处理多维数组时,我认真地问自己是否值得将这段代码移动到一个纯 .c 文件中,然后 link 它与我项目的其余部分。