【问题标题】:C++11 templated function with rvalue param call具有右值参数调用的 C++11 模板化函数
【发布时间】:2014-06-24 19:00:49
【问题描述】:

在某些 O 类中,我有模板函数 test2:

struct A{int value;};

struct O{
    A value;

    template<typename Args>
    static void test2(Args &&args){
        std::cout << std::endl << "!!!" << std::is_rvalue_reference<decltype(args)>::value << std::endl;
    }
};

然后,我想从另一个调用这个函数:

template<typename Args>
void test(Args &&args){
    using t = decltype(std::forward<Args>(args).value);
    std::cout << std::is_rvalue_reference<decltype(args)>::value;
    std::cout << std::is_rvalue_reference<decltype(std::forward<Args>(args).value)>::value;
    std::cout << std::is_rvalue_reference<t>::value;

    // All ok
    O.test2(std::forward<Args>(args).value);            

    // Alvays rvalue, even if agrs is lvalue
    O::template test2<t>(
        std::forward<t>(
            std::forward<Args>(args).value
        )
    );

   // Nor work at all, cant cast A to A&&
   O::template test2<t>(
        std::forward<Args>(args).value
    );
);

}

http://coliru.stacked-crooked.com/a/3bbf040904845a54

如果我只是传递std::forward&lt;Args&gt;(args).value 而不指定模板类型,它会正确推断类型,但是如果我必须传递类型怎么办,我应该如何调用函数呢?

我似乎无法正确手动推断类型。


更新

我需要明确指定参数,因为我有这样的功能(伪代码):

//initially call wind from somewhere.

// Tuple defined in class and is std::tuple
template<class Callback, class ...Args>
void wind(Tuple&& tuple, Callback &&callback){   
    using elementT = decltype(std::get<index>(std::forward<Tuple>(tuple)));

    ///
    /// !!! Problem here !!!
    ///
    callback.template operator()<elementT, Args...>(  std::get<index>(std::forward<Tuple>(tuple))  );              // std::get automatically return &/&&

   // recursivly call wind until the end
   wind<Callback, Args...>( std::forward<Tuple>(tuple), std::forward<Callback>(callback));
}

// callback looks like:
struct CallMe{   
  // Args provide type info. No function arguments here.
  template<class Data, class ...Args>
  void operator(Data &&data){

  }
}

这个问题与调用 - wind 和 callback() 函数有关。

【问题讨论】:

  • 当您在wind 中说data 时,您是指callback 吗?如果是这样,我认为您在那里有一些未定义的行为,因为在您在 callback 上调用 operator() 之前,callback 可能会被移动到 operator() 参数中。
  • 不,我用真实的应用程序代码更新了问题。

标签: c++ templates c++11 rvalue-reference


【解决方案1】:
template<typename Args>
static void test2(Args&& args)
{ ... }

在上面的函数中,即使参数类型看起来像一个右值引用,它也可以绑定到右值和左值参数。这通俗地称为通用参考。如果函数参数是U 类型的右值,那么T 推导出为UT&amp;&amp;U&amp;&amp;,一个右值引用,这很简单。但是,当函数参数是U类型的左值时,则T将被推导出为U&amp;,这意味着函数参数类型将是U&amp; &amp;&amp;,它经历了引用折叠 变成U&amp;。但是,要应用这些特殊规则,类型必须推导出


O::test2(std::forward<Args>(args).value); // changed O.test2 to O::test2

在这种情况下,模板参数类型是从函数参数推导出来的。参数本身是左值,所以函数参数args的类型也是左值。


O::template test2<t>(
    std::forward<t>(
        std::forward<Args>(args).value
    )
);

这里的区别在于您已经为test2 明确指定了模板参数类型。没有进行推导,在这种情况下,函数参数是一个简单的右值引用。它只能绑定到右值,并且由于外部std::forward&lt;t&gt; 强制转换(此处为t = A)而提供此右值。效果和static_cast&lt;A&amp;&amp;&gt;(value)一样。


O::template test2<t>(
    std::forward<Args>(args).value
);

最后一个案例应该已经解释了为什么这不起作用。如上所述,test2 在这种情况下只能绑定到右值,如果没有上面的第二个 std::forward,您尝试将左值绑定到右值引用参数,但失败了。

【讨论】:

  • 好吧,我可以让编译器手动推断类型吗?我真的需要传递该模板参数,因为在我的实时应用程序上,我有可变参数模板列表。
  • @tower120 您可以在最后一种情况下使用std::move(std::forward&lt;Args&gt;(args).value) 使其工作。但我不明白为什么这是必要的,即使你在这个参数后面有一个参数包。也许如果您用您正在尝试做的事情更新问题,有人可以建议另一种方式?
  • 这里使用哪种类型作为模板参数O::test2(std::forward&lt;Args&gt;(args).value);
  • “但我不明白为什么这是必要的,即使你......” - 你是什么意思?你不明白为什么函数 &&,或者为什么我必须显式指定模板参数?
  • @tower120 在这种情况下,它将被推断为左值引用。而且我不明白为什么需要指定模板参数。
【解决方案2】:

Based on knowledge that objects, inside rvalue referenced object, also rvalue referenced。 我决定结束这个(灵感来自https://stackoverflow.com/a/24083200/1559666):

template<class T, class FieldT>
using addRefU = typename std::conditional<
                            std::is_rvalue_reference<T>::value,
                            typename std::add_rvalue_reference< FieldT >::type,
                            typename std::conditional<
                                std::is_rvalue_reference<FieldT>::value,
                                typename std::add_rvalue_reference< FieldT >::type,
                                typename std::add_lvalue_reference< FieldT >::type
                            >::type
                        >::type;

    T           T::value(FieldT)    resolve
------------------------------------------------------------
 rvalue         lvalue              rvalue
 rvalue         rvalue              rvalue
 lvalue         rvalue              rvalue
 lvalue         lvalue              lvalue

using t = addRefU<decltype(args), decltype(args.value)>;
O::template test2<t>(
    static_cast<t>(args.value)
);

http://coliru.stacked-crooked.com/a/40d10f5a2f45c288

对我来说足够短。

附注如果有人对此有一些预防措施,我很乐意听取他们的意见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-27
    • 2015-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    相关资源
    最近更新 更多