带有类模板参数推论的完美转发

Vin*_*ent 5 c++ templates perfect-forwarding template-argument-deduction c++17

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

我已经把的很多&&,并std::forward在评论,因为我不知道他们需要的地方,实现完美转发。我想知道将它们放在哪里,不需要它们。

// 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/*&&*/>;
Run Code Online (Sandbox Code Playgroud)

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

Oli*_*liv 5

C ++标准定义了术语转发参考。我想通用参考用作该术语的同义词。[临时扣除电话] / 3

转发参考是一个rvalue参考,并不代表一个类模板的模板参数的cv-不合格模板参数。

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

//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&&>;
Run Code Online (Sandbox Code Playgroud)

std::forward如果的参数std::forward可以是右值引用或左值引用,则仅在函数或构造函数的内部使用有意义,具体取决于模板参数推导和引用折叠规则。如果std::forward的参数始终导致右值引用,std::move则首选,如果始终导致左值引用,则不推荐任何内容。