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

Ria*_*iaD 8 c++ templates template-meta-programming variadic-templates c++11

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

它似乎适用于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) {
Run Code Online (Sandbox Code Playgroud)

如果我理解正确的问题是,当编译器尝试计算make_vector的返回值时,它必须知道具有较少参数的向量的返回值,并且不能这样做.我该如何解决这个问题?

Bar*_*rry 7

有趣的问题!您遇到的问题是,非限定名称查找将查看其使用位置的范围(按一般性的递增顺序).但是,从[basic.scope.pdecl]:

声明的点的名称是立即其完整的声明符(第8条)后,其之前 初始化(如果有的话)

并在此功能:

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

"完整声明者"包括尾随返回类型.因此,函数模板make_vector<T, Args...>将不会在范围内,直到{.这就是为什么这不会为3个以上的参数编译.

最简单的解决将是,如果你有机会访问C++ 14,你根本就不需要尾随返回类型;

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

在name函数的主体内,您可以进行递归调用而不会出现任何问题.

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

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...);
}
Run Code Online (Sandbox Code Playgroud)


Yak*_*ont 4

创建一个名称空间以在其中放置一些帮助程序,称为details.

details创建一个类型struct adl_helper{};

创建 的实现make_vector,但其第一个参数始终是名为 的模板参数Adl。该模板参数从未被命名,并且它的实例被传递给递归。

make_vector( blah )通过调用来实现details::make_vector<T>( details::adl_helper{}, 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)...);
}
Run Code Online (Sandbox Code Playgroud)

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

首先,它在声明的地方进行评估。特别是,它在定义之前就被评估。所以它不会“抓住”自己。

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

和类型意味着此参数相关查找可以在实例化函数时看到函数本身template<class Adl>。当它查找返回类型时,它可以看到. ETC。adl_helperdetails::make_vectordetails::make_vector

活生生的例子

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

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

我认为这个解决方案更干净

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>>;
Run Code Online (Sandbox Code Playgroud)