6
 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 类型对象的右值和左值引用,以将其转发到 std::queue::push正确的版本。是像上面的代码那样做,还是在我的BlockingQueue类中提供两个版本的 push 方法?一个用于左值,一个用于右值

4

2 回答 2

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_lists 的类型),那么最好提供两种不同的重载。

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

typename std::remove_reference<U>::type>::value
^^^^^^^^
于 2013-11-01T09:15:00.243 回答
0

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

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

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

于 2013-11-01T12:13:30.037 回答