c++ - 完美转发和 std::tuple

标签 c++ tuples perfect-forwarding

考虑以下代码:

#include <iostream>
#include <tuple>
#include <utility>

// A.
template <typename... Args>
void f (const char* msg, Args&&... args)
{
    std::cout << "A. " << msg << "\n";
}

// B.
template <typename... Args>
void f (const char* msg, std::tuple<Args...>&& t)
{
    std::cout << "B. " << msg << "\n";
}

struct boo
{
    const std::tuple<int, int, long> g () const
    {
        return std::make_tuple(2, 4, 12345);
    }
};

int main ()
{
    f("First", 2, 5, 12345);
    f("Second", std::make_tuple(2, 5, 12345));

    boo the_boo;
    f("Third", the_boo.g());
    f("Fourth", std::forward<decltype(std::declval<boo>().g())>(the_boo.g()));

    return 0;
}

输出将是:

A. First
B. Second
A. Third
A. Fourth

从输出中可以明显看出它没有按照我希望的方式执行,也就是说我希望 ThirdFourth 通过 B. 函数的版本。 Fourth 调用中的 std::forward 是多余的,因为那里不会发生完美转发。为了完美转发我知道:

  • 我必须在类型推导上下文中有一个右值引用
  • 参数的类型必须是函数的模板类型

我知道这行不通。但是我没有完全掌握:

  • 为什么使用 std::tuple 改变了上下文,但它无法按预期工作?为什么模板参数不能是类型 对于另一种模板化类型?

  • 我怎样才能(优雅地)修复它?

最佳答案

您的问题是,在 Third 和 Fourth 中,您传递的是 const std::tuple其中 B. 需要一个非常量版本。

当编译器尝试为调用 f 生成代码时, 它看到您正在使用 const std::tuple 调用因此推断出 Args... 的类型成为const std::tuple .调用 B. 无效,因为该变量具有与预期不同的常量限定。

要解决这个问题,只需制作g()返回一个非常量元组。


编辑:

为了实现完美转发,您需要一个推断的上下文,正如您在问题中所说的那样。当你说 std::tuple<Args...>&&在函数参数列表中,Args...推导出来,但是 std::tuple<Args...>&&不是;它只能通过右值引用。为了解决这个问题,该参数需要采用 T&& 的形式。其中 T推导。

我们可以使用自定义类型特征来完成此操作:

template <typename T>
struct is_tuple : std::false_type {};

template <typename... Args>
struct is_tuple <std::tuple<Args...>> : std::true_type {};

然后我们使用这个特性来为元组启用一个单参数模板:

// B.
template <typename T, typename = typename std::enable_if<
                          is_tuple<typename std::decay<T>::type>::value
                          >::type>
void f (const char* msg, T&& t)
{
    std::cout << "B. " << msg << "\n";
    std::cout << "B. is lval == " << std::is_lvalue_reference<T>() << "\n";
}

或者:

//! Tests if T is a specialization of Template
template <typename T, template <typename...> class Template>
struct is_specialization_of : std::false_type {};

template <template <typename...> class Template, typename... Args>
struct is_specialization_of<Template<Args...>, Template> : std::true_type {};

template <typename T>
using is_tuple = is_specialization_of<T, std::tuple>;

is_specialization_of 取自 here并由 this question 建议.

现在我们有了完美的转发!

int main ()
{
    f("First", 2, 5, 12345);
    f("Second", std::make_tuple(2, 5, 12345));

    boo the_boo;
    f("Third", the_boo.g());
    f("Fourth", std::forward<decltype(std::declval<boo>().g())>(the_boo.g()));

    auto the_g = the_boo.g();
    f("Fifth", the_g);

    return 0;
}

输出:

A. First
B. Second
B. is lval == 0
B. Third
B. is lval == 0
B. Fourth
B. is lval == 0
B. Fifth
B. is lval == 1

关于c++ - 完美转发和 std::tuple,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28450892/

相关文章:

c++ - std::end 以 char 的原始数组结尾

c++ - 构建以元组为键的无序映射

C++ 构造函数 : Perfect forwarding and overload

c++ - 在方法调用中是否有在该对象上调用 std::forward 的感觉(与参数相反)?

c++ - 将右值作为右值转发是用例吗?

c++ - 如何读入和分离分隔的整数,例如 "100_4_1 - 15"

c++ - 如何绑定(bind)变体apply_visitor?

c++ - opencv重写一个压缩视频文件

python - 交换元组中的对象 - python

python - 使用用户定义的键返回无的列表排序