【问题标题】:Perfect forwarding in a template class [duplicate]模板类中的完美转发[重复]
【发布时间】:2021-01-02 05:40:38
【问题描述】:

我有一个模板类,在该类中我有一个带有通用/转发引用参数的静态模板函数。这个想法是完善函数的前向参数。

#include <iostream>
#include <type_traits>

//Type traits
template <typename T>
struct is_lreference_const
{
    static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};

template <typename T>
struct is_lvreference
{
    static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};

struct Bar{};

template <class... Args>
struct FooClass;

//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
  FooClass<T&&...>::impl(std::forward<T>(args)...);
}

template <typename T>
struct FooClass<T> {
    inline static void impl(T&& b) {
      if constexpr (is_lvreference<T>::value)
          std::cout << "T&" << std::endl;
      else if constexpr (is_lreference_const<T>::value)
          std::cout << "const T&" << std::endl;
      else if constexpr (std::is_rvalue_reference<T>::value)
          std::cout << "T&&" << std::endl;
      else 
          std::cout << "T" << std::endl;
  }
};

int main()
{
  const Bar b2;
  foo(b2);  
  foo(Bar{});
  Bar b;
  foo(b);
}

这一切正常,输出如预期:

const T&
T&&
T&

Demo

但是,如果我要像这样更改 foo() 函数:

template <class... T>
inline void foo(T&&... args) {
  FooClass<T...>::impl(std::forward<T>(args)...);
}

请注意,FooClass 类没有获得转发引用。那么输出是:

const T&
T
T&

即使我使用的是std::forward,右值引用的值类别也不会传递给函数impl()。为什么会这样?是因为我没有在可推断的上下文中调用impl(),因为已经为FooClass 推断了T?如何避免这样的问题?

(这个问题与今天早些时候发布的另一个 one 有关)。

编辑

我将FooClass&lt;T&gt;::impl() 更改为模板函数。然后我发现else if constexpr (std::is_rvalue_reference&lt;T&gt;::value) 从来都不是真的。我discovered 是因为右值被推断为T 类型而不是T&amp;&amp;。于是我添加了一些打印功能,发现完美转发现在可以了:

#include <iostream>
#include <type_traits>

//Type traits
template <typename T>
struct is_lreference_const
{
    static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};

template <typename T>
struct is_lvreference
{
    static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};

struct Bar{};

template <class... Args>
struct FooClass;

//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
  FooClass<T&&...>::impl(std::forward<T>(args)...);
}

template<typename T>
void printme(const T&) {
    std::cout << "constant lvalue reference" << std::endl;
}

template<typename T>
void printme(T&) {
    std::cout << "lvalue reference" << std::endl;
}

template<typename T>
void printme(T&&) {
    std::cout << "rvalue reference" << std::endl;
}


template <typename T>
struct FooClass<T> {
    template <typename Arg>
    inline static void impl(Arg&& b) {
      printme(std::forward<Arg>(b));
  }
};

int main()
{
  const Bar b2;
  foo(b2);  
  foo(Bar{});
  Bar b;
  foo(b);
}

输出:

constant lvalue reference
rvalue reference
lvalue reference

Demo

【问题讨论】:

  • T&amp;&amp; in impl(T&amp;&amp; b) 不是转发引用,它是右值引用。获得转发引用的唯一方法是使impl 成为模板本身。
  • @Evg 但是impl()是一个模板函数。
  • impl() 在您的示例中不是模板,它是类模板的非模板成员函数。
  • 只有功能模板支持完美转发。如果函数前面没有template&lt;...&gt;,那么你使用的模板参数在实例化类时就已经知道了,所以没有完美的转发。

标签: c++ templates perfect-forwarding


【解决方案1】:
template <typename T>
struct FooClass<T> {
  inline static void impl(T&& b) {

FooClass&lt;X&gt;::impl 接受一个X&amp;&amp;,其中TX

如果Xint&amp;&amp;,那么T&amp;&amp;int&amp;&amp; &amp;&amp; 这是int&amp;&amp;Tint&amp;&amp;

如果Xint,那么T&amp;&amp;int&amp;&amp; 这是int&amp;&amp;,而Tint

impl 内,您查询的是T,而不是b。因此,如果传递 FooClassintint&amp;&amp;,即使 FooClass&lt;X&gt;::impl 的签名相同,也会得到不同的值。

调用impl 之外的forwardimpl 内的代码有零影响;要么调用成功,要么不成功,你会得到一个编译错误。

没有推断impl 的参数,您是在显式设置类型。

forward 只不过是一个条件move。这不是魔法。

您可能需要重新了解 std 转发、模板函数参数推导(尤其是当您推导 T&amp;&amp; 参数时)以及引用折叠的工作原理。如果您不理解这一点,您可能会将 std forward 视为某种魔法,而实际上并非如此。

【讨论】:

  • 有道理。这根本不是完美的转发。它只是在模板类中调用函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-17
  • 2016-03-22
  • 1970-01-01
  • 2012-01-06
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
相关资源
最近更新 更多