带有类模板参数推论的完美转发 [英] Perfect forwarding with class template argument deduction

查看:79
本文介绍了带有类模板参数推论的完美转发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想了解推论指南如何与通用引用和 std :: forward 一起使用,特别是创建完美的转发包装器。下面的代码提供了一种在两种情况下使用函子包装器进行实验的代码:一种具有隐式推导指南,另一种具有显式推导指南。

I would like to understand how deductions guides work with universal references and std::forward, in particular to create perfectly forwarding wrappers. The code below provides a code to experiment with a functor wrapper in two cases: one with an implicit deduction guide and one with an explicit deduction guide.

我放了很多& std :: forward 中的注释,因为我不知道在哪里需要它们来实现完美的转发。我想知道将它们放在哪里,不需要它们。

I have put a lot of && and std::forward in comments, because I do not know where they are needed to achieve perfect forwarding. I would like to know where to put them, and where they are not needed.

// Case with not conversion constructor
template <class F>
struct functor1
{
    explicit constexpr functor1(F/*&&*/ f) 
    noexcept(std::is_nothrow_copy_constructible_v<F/*&&*/>)
    : _f(/*std::forward<F>(*/f/*)*/) 
    {}
    template <class... Args>
    constexpr operator()(Args&&... args) 
    noexcept(std::is_nothrow_invocable_v<F/*&&*/, Args/*&&*/...>)
    {
        /*std::forward<F>(*/_f/*)*/(std::forward<Args>(args)...);
    }
    private: F/*&&*/ _f;
};

// Case with a conversion constructor
template <class F>
struct functor2
{
    template <class G>
    explicit constexpr functor2(G&& g) 
    noexcept(std::is_nothrow_constructible_v<G/*&&*/, F/*&&*/>)
    : _f(/*std::forward<G>(*/g/*)*/) 
    {}
    template <class... Args>
    constexpr operator()(Args&&... args) 
    noexcept(std::is_nothrow_invocable_v<F/*&&*/, Args/*&&*/...>)
    {
        /*std::forward<F>(*/_f/*)*/(std::forward<Args>(args)...);
    }
    private: F/*&&*/ _f;
};
template <class G>
functor2(G&&) -> functor2<G/*&&*/>;

编辑:为了简单起见,由于这不是问题的重点,因此在前面的示例中,我们认为 F G 是函数对象,即具有的类/结构operator()

For the sake of simplicity, and because it is not the point of the question, in the preceding examples, we consider that F and G are function objects ie classes/structs with an operator().

推荐答案

C ++标准定义了术语转发参考。我想通用参考用作该术语的同义词。 [temp.deduct.call] / 3

The C++ standard defines the term forwarding reference. I suppose universal reference is used as a synonym for this term. [temp.deduct.call]/3


转发引用是对cv不合格模板参数的右值引用,该参数不代表的模板参数

A forwarding reference is an rvalue reference to a cv-unqualified template parameter that does not represent a template parameter of a class template.

此概念仅适用于模板函数参数或模板构造函数参数。在所有其他情况下, T& 右值引用转发参考的概念仅适用于模板参数推导。让我们考虑一下,在以下示例中,所有函数和构造函数都使用 int 自变量(独立于其常量和值类别(左值/右值))进行调用:

This concept only applies to template function argument or template constructor argument. In all other cases, T&& is a rvalue reference. The concept of forwarding reference is only usefull for template argument deduction. Let's consider that in the following examples, all the fonctions and constructors are called with an int argument (independently of its constness and value categories (lvalue/rvalue):

//possibilities of argument deduction, [cv] means any combination of "const" and "volatile": 
//  <"","const","volatile","const volatile">
template<class T> void f(T&);
  //4 possibilities: void f([cv] int&);

template<class T> void f(const T&);
  //2 possibilities: void f(const int&);
                   //void f(const volatile int&);

template<class T> void f(T&&);
  //Forwarding reference, 8 possibilities
            //void f([cv] int&);
            //void f([cv] int&&);

template<class T> void f(const T&&);
  //NOT a forwarding reference because of the const qualifier, 2 possibilities:
            //void f(const int&&);
            //void f(const volatile int&&);

template<class T>
struct S{
    template<class U>
    S(U&&);
      //Forwarding reference, 8 posibilities:
            //void S<X>([cv] int&);
            //void S<X>([cv] int&&);
      //no template argument deduction posible

    S(T&&);
      //NOT a forwarding reference, 1 possibility:
            //void S<X>(X&&);
      //Generated argument deduction:
         //template<class T> S(T&&) -> S<T>;
           //not a forwarding reference because T is a parameter of the template class; 
           //=> 4 possibilities: -> S<[cv] int&&>


    T&& a; //an rvalue reference if T is [cv] int or [cv] int&&,
           //an lvalue reference if T is [cv] int&;
           //This comes from reference colapsing rules: &+&=&; &&+&=&; &&+&&=&&       //(Nota: You may consider that a rvalue reference data member is probably a mistake)
 };

template<class U>
S(U&&) -> S<U&&>;
 //Forwarding reference, 8 possibilities:
 //   S<[cv] int&>;
 //   S<[cv] int&&>;

使用 std :: forward 仅有意义如果函数 std :: forward 的参数可以是右值引用或左值引用,则取决于模板参数推导和引用折叠规则。如果 std :: forward 的参数始终导致右值引用,则首选 std :: move ,并且它总是导致左值引用,因此不推荐使用。

Using std::forward make sense only inside the body of a function or a constructor if the argument of std::forward can either be a rvalue reference or a lvalue reference, depending on template argument deduction and reference collapsing rules. If std::forward's argument always results in a rvalue reference, std::move is prefered, and if it always results in a lvalue reference, nothing is prefered.

这篇关于带有类模板参数推论的完美转发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆