std:在模板类中转发

时间:2013-11-01 09:07:44

标签: c++ c++11 rvalue-reference

 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 :: push方法能够处理类型为T的对象的rvalue和lvalue引用,以将其转发到std::queue::push正确的版本。 是不是喜欢上面的代码,或者在我的BlockingQueue类中提供两种版本的push方法?一个用于左值,一个用于右值

2 个答案:

答案 0 :(得分:7)

实施对我来说似乎是正确的,并且完成了这项工作。

然而,在你的情况下,为左值和右值提供不同的实现可能是个好主意。 (我能想到的)主要原因是扣除模板类型参数不适用于 braced-init-lists 。考虑:

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
^^^^^^^^

答案 1 :(得分:0)

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

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

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