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的返回值时,它必须知道具有较少参数的向量的返回值,并且不能这样做.我该如何解决这个问题?
有趣的问题!您遇到的问题是,非限定名称查找将查看其使用位置的范围(按一般性的递增顺序).但是,从[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)
创建一个名称空间以在其中放置一些帮助程序,称为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)