将一组转发引用包装在一个元组中
Wrap a pack of forwarding references in a tuple
我有这样的功能
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
不再是转发引用,而是严格的右值引用。如何获得 args
的转发引用行为包裹在 tuple
中,例如接受 std::forward_as_tuple
并保留各个元组元素的引用类型。另外,在这里传递元组的最佳方式是什么,
std::tuple<Args&&...> args
或
const std::tuple<Args&&...>& args
或
std::tuple<Args&&...>&& args
?
我是否需要在函数内的元组元素上使用 std::forward
,或者只是 std::get
它们?
My understanding is that in the std::tuple<Args&&...>
the Args
are not forwarding references anymore
正确。
but strictly rvalue references
是的,除非明确指定 Args
,在这种情况下 reference collapsing 可以将它们变成左值引用,即 foo<int&>(...)
将导致 Args&& -> int& && -> int&
。
what is the best way to pass the arguments in a tuple.
这取决于 foo
的预期用途。如果你不需要知道 Args...
到底是什么,你可能会逃脱:
template <typename Tuple>
void foo(Tuple&& args, const std::string& name = {});
在这种情况下,仍然可以使用 std::tuple_element_t<N, std::decay_t<Tuple>>
.
访问个别类型
如果你做想知道foo
里面的Args...
(没有任何额外的抽象层次),你可能想推断出确切的类型,没有任何参考意义:
template <typename.... Args>
void foo(std::tuple<Args...>&& args, const std::string& name = {});
请注意,如果有人在 std::forward_as_tuple
中使用左值和右值,则值类别将存储在 Args
中,您仍然可以使用 std::forward
转发这些参数(std::forward
不仅限于转发引用,将其视为条件转换)。
Also, what's the best way to pass the tuple here
可能 Tuple&&
如前所述。如果没有,那么这又取决于用法。如果您使用 const std::tuple<Args...>&
,那么通过查看 std::get
的重载列表,您将看到值类别和常量传播到 return 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");
}
我有这样的功能
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
不再是转发引用,而是严格的右值引用。如何获得 args
的转发引用行为包裹在 tuple
中,例如接受 std::forward_as_tuple
并保留各个元组元素的引用类型。另外,在这里传递元组的最佳方式是什么,
std::tuple<Args&&...> args
或
const std::tuple<Args&&...>& args
或
std::tuple<Args&&...>&& args
?
我是否需要在函数内的元组元素上使用 std::forward
,或者只是 std::get
它们?
My understanding is that in the
std::tuple<Args&&...>
theArgs
are not forwarding references anymore
正确。
but strictly rvalue references
是的,除非明确指定 Args
,在这种情况下 reference collapsing 可以将它们变成左值引用,即 foo<int&>(...)
将导致 Args&& -> int& && -> int&
。
what is the best way to pass the arguments in a tuple.
这取决于 foo
的预期用途。如果你不需要知道 Args...
到底是什么,你可能会逃脱:
template <typename Tuple>
void foo(Tuple&& args, const std::string& name = {});
在这种情况下,仍然可以使用 std::tuple_element_t<N, std::decay_t<Tuple>>
.
如果你做想知道foo
里面的Args...
(没有任何额外的抽象层次),你可能想推断出确切的类型,没有任何参考意义:
template <typename.... Args>
void foo(std::tuple<Args...>&& args, const std::string& name = {});
请注意,如果有人在 std::forward_as_tuple
中使用左值和右值,则值类别将存储在 Args
中,您仍然可以使用 std::forward
转发这些参数(std::forward
不仅限于转发引用,将其视为条件转换)。
Also, what's the best way to pass the tuple here
可能 Tuple&&
如前所述。如果没有,那么这又取决于用法。如果您使用 const std::tuple<Args...>&
,那么通过查看 std::get
的重载列表,您将看到值类别和常量传播到 return 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");
}