如何在保留单个参数的同时传递可变参数列表
How do I pass on a variadic argument list while keeping a single argument
假设我有一个基数 class,以后可能会从它派生出 "extended",我们称它为 class Base
,扩展名 Derived
. classes 的模板签名是固定的,不能更改(即我们不能更改 classes 的模板参数)。 Derived
class 的作者对 Base
一无所知,只知道它 可能 将一些参数传递给它的构造函数。
然而,最终派生的调用者class知道应该传递多少参数。我该如何编写这个 Derived
扩展名?这是我拥有的:
struct Base
{
Base(int baseArg) {}
};
struct Derived : public Base
{
template <typename... Args>
Derived(Args&&... args, int derivedArg)
:
Base(std::forward<Args>(args)...)
{
}
};
当我尝试 run this 和 Derived d(1, 1);
时,我收到以下错误消息:
prog.cpp: In function 'int main()':
prog.cpp:19:16: error: no matching function for call to 'Derived::Derived(int, int)'
Derived d(1, 1);
^
prog.cpp:19:16: note: candidates are:
prog.cpp:11:2: note: template<class ... Args> Derived::Derived(Args&& ..., int)
Derived(Args&&... args, int myArg)
^
prog.cpp:11:2: note: template argument deduction/substitution failed:
prog.cpp:19:16: note: candidate expects 1 argument, 2 provided
Derived d(1, 1);
^
prog.cpp:8:8: note: constexpr Derived::Derived(const Derived&)
struct Derived : public Base
^
prog.cpp:8:8: note: candidate expects 1 argument, 2 provided
prog.cpp:8:8: note: constexpr Derived::Derived(Derived&&)
prog.cpp:8:8: note: candidate expects 1 argument, 2 provided
Derived
的构造函数应采用 2 个参数,使用第一个参数构造自身并将第二个参数传递给基础 class。为什么这不起作用?
Derived
构造函数是非推导上下文。来自 [temp.deduct.type],§14.8.2.5/5:
The non-deduced contexts are:
— [ .. ]
— A function parameter pack that does not occur at the end of the parameter-declaration-list.
在你的例子中,参数包不在最后 - 它后面还有一个参数,所以它是一个非推导上下文,这使得你的程序格式错误。简单的解决方案是反转顺序:
template <typename... Args>
Derived(int derivedArg, Args&&... args) // totally OK
:
Base(std::forward<Args>(args)...)
{
}
nth
获取某些参数的第n个元素:
template<size_t n, class...Args>
auto nth( Args&&... args )
->typename std::tuple_element<n,std::tuple<Args&&...>>::type
{
return std::get<n>( std::forward_as_tuple(std::forward<Args>(args)...) );
}
这使用上面的方法来提取最后一个参数并将它们发送到适当的位置:
struct Derived : public Base {
struct tag{};
template <typename... Args>
Derived(Args&&... args) : Derived(
tag{},
std::make_index_sequence<sizeof...(Args)-1>{},
std::forward<Args>(args)...
){}
template<size_t...Is, class...Args>
Derived(tag, std::index_sequence<Is...>, Args&&...args ):
Base(nth<Is>(std::forward<Args>(args)...)...)
{
int derivedArg = nth<sizeof...(Args)-1>(std::forward<Args>(args)...);
}
};
我们为前 n-1 个元素构建一个序列,将它们传递给 base,并为我们自己存储最后一个元素。
不过,如果将额外的参数放在首位会容易得多。
假设我有一个基数 class,以后可能会从它派生出 "extended",我们称它为 class Base
,扩展名 Derived
. classes 的模板签名是固定的,不能更改(即我们不能更改 classes 的模板参数)。 Derived
class 的作者对 Base
一无所知,只知道它 可能 将一些参数传递给它的构造函数。
然而,最终派生的调用者class知道应该传递多少参数。我该如何编写这个 Derived
扩展名?这是我拥有的:
struct Base
{
Base(int baseArg) {}
};
struct Derived : public Base
{
template <typename... Args>
Derived(Args&&... args, int derivedArg)
:
Base(std::forward<Args>(args)...)
{
}
};
当我尝试 run this 和 Derived d(1, 1);
时,我收到以下错误消息:
prog.cpp: In function 'int main()':
prog.cpp:19:16: error: no matching function for call to 'Derived::Derived(int, int)'
Derived d(1, 1);
^
prog.cpp:19:16: note: candidates are:
prog.cpp:11:2: note: template<class ... Args> Derived::Derived(Args&& ..., int)
Derived(Args&&... args, int myArg)
^
prog.cpp:11:2: note: template argument deduction/substitution failed:
prog.cpp:19:16: note: candidate expects 1 argument, 2 provided
Derived d(1, 1);
^
prog.cpp:8:8: note: constexpr Derived::Derived(const Derived&)
struct Derived : public Base
^
prog.cpp:8:8: note: candidate expects 1 argument, 2 provided
prog.cpp:8:8: note: constexpr Derived::Derived(Derived&&)
prog.cpp:8:8: note: candidate expects 1 argument, 2 provided
Derived
的构造函数应采用 2 个参数,使用第一个参数构造自身并将第二个参数传递给基础 class。为什么这不起作用?
Derived
构造函数是非推导上下文。来自 [temp.deduct.type],§14.8.2.5/5:
The non-deduced contexts are:
— [ .. ]
— A function parameter pack that does not occur at the end of the parameter-declaration-list.
在你的例子中,参数包不在最后 - 它后面还有一个参数,所以它是一个非推导上下文,这使得你的程序格式错误。简单的解决方案是反转顺序:
template <typename... Args>
Derived(int derivedArg, Args&&... args) // totally OK
:
Base(std::forward<Args>(args)...)
{
}
nth
获取某些参数的第n个元素:
template<size_t n, class...Args>
auto nth( Args&&... args )
->typename std::tuple_element<n,std::tuple<Args&&...>>::type
{
return std::get<n>( std::forward_as_tuple(std::forward<Args>(args)...) );
}
这使用上面的方法来提取最后一个参数并将它们发送到适当的位置:
struct Derived : public Base {
struct tag{};
template <typename... Args>
Derived(Args&&... args) : Derived(
tag{},
std::make_index_sequence<sizeof...(Args)-1>{},
std::forward<Args>(args)...
){}
template<size_t...Is, class...Args>
Derived(tag, std::index_sequence<Is...>, Args&&...args ):
Base(nth<Is>(std::forward<Args>(args)...)...)
{
int derivedArg = nth<sizeof...(Args)-1>(std::forward<Args>(args)...);
}
};
我们为前 n-1 个元素构建一个序列,将它们传递给 base,并为我们自己存储最后一个元素。
不过,如果将额外的参数放在首位会容易得多。