3

私はC++11を使用しています。std::queue<T>の単純なラッパーを作成し、コピー コンストラクターを持たないクラスで使用すると 、コンパイル エラーが発生しました。

以下は、問題を説明するためのスニペットです。

基本的に、 のラッパーがstd::queue<T>あり、これには の 2 つの仮想オーバーロードがあり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=ofを上書きするだけでmyqueue問題ありません。

于 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-copyableTには何もないからです。または、 non-copyable の特殊化で問題のあるメソッドを純粋仮想にすることもできますT

申し訳ありませんが、icpc を修正することはできませんが、少なくともこのコードは合法的な C++11 のようです。

于 2013-06-26T12:40:59.857 に答える