Interesting question! The problem you're running into is that unqualified name lookup will look in the scopes of where it is used (in increasing order of generality). But, from [basic.scope.pdecl]:
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its
initializer (if any)
and in this function:
template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes)
-> std::vector<decltype(make_vector<T>(sizes...))> {
...
}
The "complete declarator" includes the trailing-return-type. So the function template make_vector<T, Args...>
will not be in scope yet until the {
. That's why this won't compile for 3+ arguments.
The simplest fix would be if you had access to C++14, you simply wouldn't need the trailing return type;
template <typename T, typename... Args>
auto make_vector(std::size_t first, Args... sizes)
{ /* exactly as before */ }
Within the body of the name function, you can make the recursive call with no issues.
Without C++14, you could forward it to a class template whose name will be in the scope of all the recursive calls:
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...);
}
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…