在元组

时间:2017-02-16 02:55:44

标签: c++ tuples c++14 perfect-forwarding forwarding-reference

我有这样的功能

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它们?

1 个答案:

答案 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");
}

DEMO