我有这样的功能
template <typename... Args> void foo(Args&&... args);
我需要在末尾添加一个带有默认参数的额外参数。由于包装需要最后,我想改变功能
template <typename... Args> void foo(std::tuple<Args&&...> args,
const std::string& name = {});
问题是,在tuple
中传递参数的最佳方法是什么。
我的理解是,std::tuple<Args&&...>
Args
不再转发引用,而是严格的rvalue引用。如何获取args
中包含的tuple
的转发引用行为,例如接受std::forward_as_tuple
并保留单个元组元素的引用类型。此外,在这里传递元组的最佳方法是什么,
std::tuple<Args&&...> args
或
const std::tuple<Args&&...>& args
或
std::tuple<Args&&...>&& args
我是否需要在函数内部的元组元素上使用std::forward
,或者只是std::get
它们?
答案 0 :(得分:1)
我的理解是
std::tuple<Args&&...>
Args
不再转发参考
正确。
但严格的右值引用
是的,除非明确指定Args
,否则reference collapsing可以将它们转换为左值引用,即foo<int&>(...)
将导致Args&& -> int& && -> int&
。
在元组中传递参数的最佳方法是什么。
这取决于foo
的预期用途。如果你不需要知道Args...
到底是什么,你可能会逃脱:
template <typename Tuple>
void foo(Tuple&& args, const std::string& name = {});
在这种情况下,仍然可以使用std::tuple_element_t<N, std::decay_t<Tuple>>
访问各种类型。
如果你做想知道Args...
内的foo
(没有任何额外的抽象级别),你可能想要推断出确切的类型,没有任何参考:< / p>
template <typename.... Args>
void foo(std::tuple<Args...>&& args, const std::string& name = {});
请注意,如果有人在std::forward_as_tuple
内部使用了左值和右值,则值类别将存储在Args
中,您仍然可以使用std::forward
转发这些参数(std::forward
是不仅限于转发引用,将其视为条件转换。)
此外,在这里传递元组的最佳方法是
如前所述,可能Tuple&&
。如果没有,那么它又取决于使用情况。如果您使用const std::tuple<Args...>&
,那么通过查看std::get
的重载列表,您会看到值类别和常量传播到std::get
的返回值(模数) reference collapsing)。与std::tuple<Args...>&&
相同。此外,使用后者,您将不得不使用元组右值作为参数(foo(std::forward_as_tuple(...), ...)
而不是foo(my_tuple, ...)
)。
另一种解决方案是接受参数包,并检测最后一个参数是否可以被const std::string&
绑定:
#include <string>
#include <utility>
#include <tuple>
#include <type_traits>
struct dummy {};
template <typename... Args>
void foo_impl(Args&&... args)
{
const std::string& s = std::get<sizeof...(Args) - 1>(std::forward_as_tuple(std::forward<Args>(args)...));
}
template <typename... Args>
auto foo(Args&&... args)
-> std::enable_if_t<std::is_constructible<std::string, std::tuple_element_t<sizeof...(Args), std::tuple<dummy, Args...>>>{}>
{
foo_impl(std::forward<Args>(args)...);
}
template <typename... Args>
auto foo(Args&&... args)
-> std::enable_if_t<!std::is_constructible<std::string, std::tuple_element_t<sizeof...(Args), std::tuple<dummy, Args...>>>{}>
{
foo_impl(std::forward<Args>(args)..., "default");
}