3

我正在使用 C++11。std::queue<T>在为 编写简单的包装器并将其与没有复制构造函数的类一起使用 时,我遇到了编译错误。

以下是说明问题的片段。

基本上,我有一个包装器std::queue<T>,它有两个虚拟重载push

#include <queue>
#include <utility>
#include <future>

template <typename T>
class myqueue {
public:
    myqueue() : q() {}
    virtual ~myqueue() {}

    // pushes a copy
    virtual void push(const T& item) {
        q.push(item);
    }
    // pushes the object itself (move)
    virtual void push(T&& item) {
        q.push(std::move(item));
    }
private:
    std::queue<T> q;
};

int main() {
    // Thanks to Yakk for pointing out that I can reduce clutter by using one of std's non-copyable classes!
    myqueue<std::packaged_task<int()>> q;
    std::packaged_task<int()> t([]{return 42;});
    q.push(std::move(t));
}

当我尝试编译它时(这里我的 Linux 机器上的 ICC 13.2、g++ 4.7.3,以及 Ideone 上的 g++ 4.7.2:http://ideone.com/HwBhIX ,编译器抱怨它无法实例化 myqueue:: push(const nocopy&) 因为 nocopy 的拷贝构造函数被删除了。

如果我virtual从 中删除修饰符push,则可以正常编译(在我的机器和 Ideone 上)。

谁能帮我理解为什么会这样?


PS:这是来自 Ideone 的错误堆栈:

In file included from /usr/include/c++/4.7/i486-linux-gnu/bits/c++allocator.h:34:0,
                 from /usr/include/c++/4.7/bits/allocator.h:48,
                 from /usr/include/c++/4.7/deque:62,
                 from /usr/include/c++/4.7/queue:61,
                 from prog.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::packaged_task<int()>; _Args = {const std::packaged_task<int()>&}; _Tp = std::packaged_task<int()>]’:
/usr/include/c++/4.7/bits/stl_deque.h:1376:6:   required from ‘void std::deque<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::packaged_task<int()>; _Alloc = std::allocator<std::packaged_task<int()> >; std::deque<_Tp, _Alloc>::value_type = std::packaged_task<int()>]’
/usr/include/c++/4.7/bits/stl_queue.h:212:9:   required from ‘void std::queue<_Tp, _Sequence>::push(const value_type&) [with _Tp = std::packaged_task<int()>; _Sequence = std::deque<std::packaged_task<int()>, std::allocator<std::packaged_task<int()> > >; std::queue<_Tp, _Sequence>::value_type = std::packaged_task<int()>]’
prog.cpp:13:9:   required from ‘void myqueue<T>::push(const T&) [with T = std::packaged_task<int()>]’
prog.cpp:28:1:   required from here
/usr/include/c++/4.7/ext/new_allocator.h:110:4: error: use of deleted function ‘std::packaged_task<_Res(_ArgTypes ...)>::packaged_task(const std::packaged_task<_Res(_ArgTypes ...)>&) [with _Res = int; _ArgTypes = {}; std::packaged_task<_Res(_ArgTypes ...)> = std::packaged_task<int()>]’
In file included from prog.cpp:3:0:
/usr/include/c++/4.7/future:1337:7: error: declared here
4

2 回答 2

2

myqueue的标准复制构造函数调用 的复制构造函数nocopy。只需覆盖或=delete复制构造函数和operator=ofmyqueue就可以了。

于 2013-06-26T12:42:25.063 回答
1

出现这种情况是因为 gcc 4.7 提供的 c++11 实现不完整。使用 c++11 时,您必须始终使用最新的编译器版本。clang 通常是最完整的,其次是 gcc,稍稍落后的是 icpc。


在您进行编辑后,代码不再编译(clang 3.2 或 gcc 4.8),因为它完全是错误的。当模板类myqueue<std::packaged_task<int()>>被实例化时,非模板成员virtual void push(const T&)也被实例化。然而,这个成员调用了已删除的复制构造函数T,所以是非法的,错误。(它为非虚拟编译push(const T&)是危险的,因为一旦你尝试使用该函数,你就会得到一个错误。)

为了让你的代码工作,你必须避免这种情况。virtual成员不能是模板(这可以让您通过 SFINAE 避免问题)。class myqueue<T>但是您可以根据是否可复制来进行专业化T。以下代码使用 gcc 4.8 编译(但不能使用 icpc 13 或 clang 3.2,它们的实现有误std::is_copy_constructible)。

#include <queue>
#include <type_traits>
#include <utility>
#include <future>

template <typename T, typename E=void> class myqueue;

template <typename T>
class myqueue<T, typename std::enable_if<std::is_copy_constructible<T>::value>::type>
{
  std::queue<T> q;
public:
  virtual ~myqueue() {}
  // pushes a copy
  virtual void push(const T& item) { q.push(item); }
  // pushes the object itself (move)
  virtual void push(T&& item) { q.push(std::move(item)); }
};

template <typename T>
class myqueue<T,typename std::enable_if<!std::is_copy_constructible<T>::value>::type>
{
  std::queue<T> q;
public:
  virtual ~myqueue() {}
  // pushes the object itself (move)
  virtual void push(T&& item) { q.push(std::move(item)); }
};

int main() {
  std::packaged_task<int()> t([]{return 42;});
  myqueue<std::packaged_task<int()>> q;
  q.push(std::move(t));
} 

virtual void push(const&T)当然,这与您的原始代码并不完全相同,因为non-copyable不会有T,但我认为这正是您想要的。或者,您可以在 non-copyable 的专业化中将有问题的方法设为纯虚拟方法T

抱歉,我无法为您修复 icpc,但至少这段代码似乎是合法的 C++11。

于 2013-06-26T12:40:59.857 回答