从仿函数类模板参数推导出的(衰减的)类型时,完美转发失败。为什么?

问题描述 投票:0回答:2

在下面的示例中,

#include <iostream>
#include <utility>
//okay:
//
template<typename T, typename F>
decltype(auto) runner(T&& t, F f)
{
    return f(std::forward<T>(t));
}

//okay:
//
struct runner_t_f {
    template<typename T>
    void operator()(T&& t)
        {
            std::cout<<"template op(): "<<t<<'\n';
        }
};

template<typename T>
struct runner_t {
    void operator()(T&& t) //error: cannot bind 'T' lvalue to 'T&&'
        {
            std::cout<<"template functor: "<<t<<'\n';
        }
};

int main(void)
{
    int j{13};

    auto res = runner(j, [](int const& x) -> double { //okay
            return static_cast<double>(x*x);
        });
    std::cout<<res<<'\n';

    runner_t_f rtf{};
    rtf(j);//okay

    runner_t<int> rt{};
    rt(j);//not okay...why?
    //error: cannot bind ‘int’ lvalue to ‘int&&’

    return 0;
}

我尝试使用完美的转发来创建这些runner蹦床功能。除runner_t之外,所有编译(和执行)操作均失败,并出现以上代码中注释的错误。为什么?

而且我该如何模拟编译器正在尝试执行的操作,以便我可以理解出了什么问题?预先感谢。

c++ perfect-forwarding
2个回答
1
投票

由于引用折叠和模板类型推导规则,转发引用起作用。

在一个简单的函数中:

template <typename T>
void foo(T&& t) {}
  • [当称为foo(42)时,推导Tint,因此T&&变为int&&:rvalue-reference-to- int
  • [当称为int i = 42; foo(i)时,推导Tint&,并且T&&变为int& &&。由于您没有对引用的引用,因此引用折叠规则会出现,并为您留下int&:lvalue-reference-to- int

这意味着什么:

template<typename T>
struct runner_t {
    void operator()(T&& t) //error: cannot bind 'T' lvalue to 'T&&'
        {
            std::cout<<"template functor: "<<t<<'\n';
        }
};
  • 当您指定runner_t<int>时,T&&变为int&&,并且右值引用不能绑定到j,因为它是左值。

  • 如果您改为将rt的类型指定为runner_t<int&>,则t的类型将为int& &&,这将折叠为int&。可以绑定到j


0
投票

要模拟编译器在做什么,您将需要这样的东西:

    runner_t<decltype((j))> rt{};
    rt(j);

我们需要传递表达式j的类型(我们使用多余的括号来完成)。这是一个左值,因此您得到T = int&T&& = int&,我们可以将j传递为。

以前没有用,因为对于T = intT&& = int&&,它只能绑定到右值,因此您必须执行类似rt(int{j})的操作(顺便说一下,decltype((int{j})) = int,所以runner_t<decltype((int{j}))>{}(int{j})仍会工作)

© www.soinside.com 2019 - 2024. All rights reserved.