带有右值参数调用的C++11模板函数

C++11 templated function with rvalue param call

本文关键字:C++11 函数 调用 值参 参数      更新时间:2023-10-16

在一些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<Args>(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((函数有关。

template<typename Args>
static void test2(Args&& args)
{ ... }

在上面的函数中,即使参数类型看起来像右值引用,它也可以绑定到右值和左值参数。这通常被称为通用引用。如果函数自变量是类型为U的右值,则T被推导为U,而T&&U&&,一个右值引用,这是直接的。然而,当函数参数是类型为U的左值时,则T将被推导为U&,这意味着函数参数类型将是U& &&,其经历引用折叠而变为U&。但是,为了应用这些特殊规则,必须推导出类型


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<t>强制转换(此处为t = A(,您为它提供了此右值。其效果与static_cast<A&&>(value)相同。


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

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

基于对象的知识,在右值内引用对象,也引用右值。我决定以这个结尾(灵感来自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

对我来说足够短。

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