模板完美转发class

Perfect forwarding in a template class

我有一个模板 class,在该模板中 class 我有一个带有 universal/forwarding 参考参数的静态模板函数。这个想法是完善函数的前向参数。

#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 class 没有获得转发参考。那么输出为:

const T&
T
T&

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

(此问题与今天早些时候发布的另一个 相关)。

编辑

我把FooClass<T>::impl()改成了模板函数。然后我发现 else if constexpr (std::is_rvalue_reference<T>::value) 从来都不是真的。我 discovered 这是因为右值被推断为 T 而不是 T&& 类型。所以我添加了一些打印功能,发现完美转发现在可以工作了:

#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

template <typename T>
struct FooClass<T> {
  inline static void impl(T&& b) {

FooClass<X>::impl 接受一个 X&&,其中 TX.

如果Xint&&,那么T&&就是int&& &&,也就是int&&T就是int&&

如果Xint,那么T&&就是int&&也就是int&&,而T就是int .

impl 中,您查询 T,而不是 b。因此,如果您传递 FooClass 一个 intint&&,您将获得不同的值,即使 FooClass<X>::impl 的签名相同。

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

不是推导参数impl,您是在明确设置类型。

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

您可能需要复习一下标准转发、模板函数参数推导(尤其是当您推导 T&& 参数时)以及引用折叠的工作原理。如果您不明白这一点,您可能会将 std forward 视为某种魔法,但事实并非如此。