3

別のスレッドでクラスを実行し、メインスレッドから制御 (一時停止、再開、停止など) できるクラスランナー (固定時間でクラスを実行する) を作成しようとしています。

そこで、C++11 の Functor などの機能を活用したいと考えています。しかし、奇妙な問題があります。ランナーに渡されたファンクターのデストラクタが 2 回呼び出されています。

#include <iostream>
#include <chrono>
#include <thread>

using namespace std;

class Runner {
 public:
  typedef function<bool()> fn_t;
  Runner(fn_t &&fn) : fn_(move(fn)), thread_(Thread, ref(*this)) {
    cout << "Runner" << endl;
  }
  ~Runner() {
    cout << "~Runner" << endl;
    thread_.join();
  }
 private:
  fn_t fn_;
  thread thread_;
  static void Thread(Runner &runner) {
    while (runner.fn_()) {
      cout << "Running" << endl;
      this_thread::sleep_for(chrono::milliumseconds(1));
    }
  }
};

class Fn {
 public:
  Fn() : count(0) {
    cout << "Fn" << endl;
  }
  ~Fn() {
    cout << "~Fn" << endl;
  }
  bool operator()() {
    return (++count < 5);
  }
 private:
  int count;
};

int main (int argc, char const* argv[])
{
  Fn fn;
  Runner runner(move(fn));
  return 0;
}

出力:

Fn
Runner
~Fn
~Runner
Running
Running
Running
Running
Running
~Fn
~Fn

もし私が変われば

Fn fn;
Runner runner(move(fn));

Runner runner(Fn());

プログラムは何も出力せず、失速します。コンパイルの最適化を無効にしようとしましたが、何も変わりません。説明はありますか?

どうすればこれを修正できますか、または他の方法で同じことを行うことができますか? このクラスを std::async / std::thread のように実装する必要がありますか?

に更新 Runner runner(Fn())

このステートメントは、関数宣言として中断されました。

Runner runner((Fn()))問題を解決しました。

すべてのコメントと回答に感謝します。rvalueを調べたところ、グラウンド 0 からの右辺値参照の意味を誤解しているようです。他の方法を試してみます。

この問題の最終的な解決策

#include <iostream>
#include <chrono>
#include <thread>
#include <vector>

using namespace std;

template<typename T, typename... Args>
class Runner {
 public:
  Runner(Args&&... args) : 
      t(forward<Args>(args)...), 
      thread_(Thread, ref(*this)) {
    cout << "Runner" << endl;
  }
  ~Runner() {
    cout << "~Runner" << endl;
    thread_.join();
  }
 private:
  T t;
  thread thread_;
  static void Thread(Runner &runner) {
    while (runner.t()) {
      cout << "Running" << endl;
      this_thread::sleep_for(chrono::milliseconds(100));
    }
  }
};

class Fn {
 public:
  Fn() : count(0) {
    cout << "Fn" << endl;
  }
  ~Fn() {
    cout << "~Fn" << endl;
  }
  bool operator()() {
    return (count++ < 5);
  }
 private:
  int count;
};

int main (int argc, char const* argv[])
{
  //vector<Fn> fns;
  //fns.emplace_back(Fn());
  Runner<Fn> runner;
  return 0;
}

出力:

Fn
Runner
~Runner
Running
Running
Running
Running
Running
~Fn
4

2 に答える 2

4

まず第一に、独自のムーブ コンストラクターを除いて、ほとんどの部分で右辺値参照引数を取らないでください。std::function<bool()>あなたが持っているように、 の左辺値をのコンストラクターに渡す方法はありませんRunner

int main()
{
    Fn fn;
    std::function<bool()> func(fn);
    Runner runner(func); // this is illegal
}

多分私は十分に創造的ではありませんが、あなたがそのようなことを防ぎたい正当な理由を想像することはできません.

std::function独自のコピー/移動を処理する必要があります。オブジェクトのコピーが必要な場合は、パラメーターを値で取得します。関数に右辺値が渡された場合、移動構築されます。左辺値が渡された場合、コピーが構築されます。次に、Runnerコンストラクターで、fontanini が示したように、値をメンバー オブジェクトに移動できます。

ただし、オブジェクトを移動すると、まだ 2 番目のオブジェクトが作成されており、2 番目のオブジェクトを破棄する必要があるため、デストラクタの呼び出しを減らすことは保証されていません。破壊を少なくするには、コピーの省略を行う必要があります。これにより、実際には複数のオブジェクトの作成が回避されます。ただし、移動とは異なり、これは実装の問題であり、希望するすべての状況で有効になるとは限りません。

于 2012-05-16T20:20:25.903 に答える
4

使用std::move:

Runner(fn_t &&fn) : fn_(std::move(fn)), thread_(Thread, ref(*this)) {
    /*....*/
}

を明示的に使用する必要がありますstd::move。そうしないと、const 参照として扱われます。次を使用することもできますstd::forward

Runner(fn_t &&fn) : fn_(std::forward<fn_t>(fn)), thread_(Thread, ref(*this)) {
    /*....*/
}
于 2012-05-16T19:12:54.967 に答える