本文介绍了完美转发和 std::tuple的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下代码:

#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 是多余的,因为那里不会发生完美的转发.为了有完美的转发,我知道:

From the output it's evident that it does not do what I would like it to do, that is I would like Third and Fourth to go through the B. version of the function.The std::forward from the Fourth call is superfluous as perfect forwarding does not happen there. In order to have perfect forwarding I know:

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

我知道它不起作用.但我没有完全掌握:

I understand it does not work. But I do not fully grasp:

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

  • why the context is changed by using std::tuple in such a way that it fails to work as desired ? Why the template parameter cannot be the typefor another templated type?

我该如何(优雅地)修复它?

how can I(elegantly) fix it ?

推荐答案

你的问题是在第三和第四你传递了一个 const std::tuple 其中 B. 期望一个非常量版本.

Your issue is that in Third and Fourth you are passing a const std::tuple where B. expects a non-const version.

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

When the compiler attempts to generate code for the call to f, it sees that you are calling with a const std::tuple and so deduces the type of Args... to be const std::tuple. Calling B. is not valid because the variable has a different const-qualification than expected.

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

To solve this, just make g() return a non-const tuple.

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

In order for perfect forwarding to occur, you need a deduced context, as you say in your question. When you say std::tuple<Args...>&& in the function argument list, Args... is deduced, but std::tuple<Args...>&& is not; it can only by an rvalue reference. In order to fix this, that argument needs to be of the form T&& where T is deduced.

我们可以使用自定义类型特征来实现:

We can accomplish this using a custom type trait:

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

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

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

Then we use this trait to enable a single-argument template for tuples only:

// 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 取自 此处 并由 this 建议问题.

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

Now we have perfect forwarding!

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

这篇关于完美转发和 std::tuple的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 10:14