std:forward 在模板类中

std:forward inside a template class

本文关键字:forward std      更新时间:2023-10-16
 template<typename T>
   class BlockingQueue
   { 
       std::queue<T> container_;
       template< typename U >
       void push(U&& value)
       {
           static_assert(std::is_same<T, typename std::remove_reference<U>::type>::value,"Can't call push without the same parameter as template parameter's class");
           container_.push(std::forward<U>(value)); 
       }
};

我希望 BlockingQueue::p ush 方法能够处理 T 类型的对象的 rvalue 和 lvalue 引用,以将其转发到正确的版本std::queue::push。最好像上面的代码一样做,还是在我的BlockingQueue类中提供两个版本的推送方法?一个用于左值,一个用于右值

实现对我来说

似乎是正确的,并且可以完成工作。

不过,在您的情况下,为左值和右值提供不同的实现可能是一个好主意。主要原因(我能想到的)是模板类型参数的推导不适用于大括号初始化列表。考虑:

struct foo {
    foo(std::initializer_list<int>) {
    }
};
// ...
foo f{1, 2, 3};    // OK
BlockingQueue<foo> b;

使用OP的代码(*)

b.push(f);         // OK
b.push({1, 2, 3}); // Error

如果相反,则提供以下BlockingQueue::push重载:

void push(const T& value) {
    container_.push(value); 
}
void push(T&& value) {
    container_.push(std::move(value)); 
}

然后,曾经失败的行将正常工作。

相同的参数适用于聚合。例如,如果foo被定义为

struct foo {
    int a, b, c;
};

人们会观察到上述相同的行为。

我的结论是,如果您希望BlockingQueue支持更多类型(包括集合或构造函数采用 std::initializer_list s 的类型),那么最好提供两种不同的重载。

(*) OP代码中的一个小更正:在static_assert中你需要使用typename

typename std::remove_reference<U>::type>::value
^^^^^^^^
如果你想

使用完美的转发,那么我建议你使用emplace queue类的方法。 emplace方法将给定的参数转发给构造函数T。无需检查T是否与U相同。只要T可以从U构造,它就应该编译。此外,如果您愿意,您可以使用可变参数模板参数。

template<typename... Args>
void push(Args&&... args)
{
    container_.emplace(std::forward<Args>(args)...);
}

因此,你可以推送任何你想要的东西,只要T是从给定的参数构造的。