简体   繁体   English

使用std :: vector :: emplace_back

[英]Use of std::vector::emplace_back

I have some class-warper for std::thread. 我对std :: thread有一些要求。 Here is the constructor: 这是构造函数:

template <typename Function, typename... Args>
InterruptibleThread(Function&& fun, Args&&... args)
{
    _thread = std::thread([](std::atomic_bool * f, Function&& function, Args&&... arguments)
    {
        _flag_ref = f;
        (function)(std::forward<Args>(arguments)...);
    },
        &_flag,
        std::forward<Function>(fun)
        , std::forward<Args>(args)...
        );
}

And then I'm using it so (example): InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it)) 然后我就这样使用它(示例):InterruptibleThread(&SourceImageList :: StartFrameProcessingStatic,this,std :: ref(it))

The compiler builds this code successfuly. 编译器会成功构建此代码。 But now I would like to make a vector of such objects: 但是现在我想对这些对象做一个向量:

std::vector<InterruptibleThread> grp;

I would like to allocate it on the stack so what I'm doing is: 我想在堆栈上分配它,所以我正在做的是:

grp.emplace_back(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it));

And I'm getting this error: 我收到此错误:

C2064   term does not evaluate to a function taking 0 arguments

Here are the options which are validated by the compiler: 以下是编译器验证的选项:

1) grp.push_back(new InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it)));
2) grp.push_back(InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it)));

But the first one is allocating an object on the heap so I need to release it manually, and the second one makes a copy of the object. 但是第一个是在堆上分配一个对象,因此我需要手动释放它,而第二个则制作该对象的副本。

Can I use emplace_back here (compiler is MSVC 2015 update 3)? 我可以在这里使用emplace_back (编译器是MSVC 2015更新3)吗?

Update 更新资料

Ok, I made some fixes based on answers. 好的,我根据答案做了一些修复。 Here is the final version of this class: 这是此类的最终版本:

#pragma once
#include <exception>
#include <atomic>
#include <thread>
#include <future>
#include <windows.h>
// Synopsis
class InterruptThreadException;
class InterruptibleThread;

// Interrupt exception
class InterruptThreadException : public virtual std::exception {
public:
    virtual char const* what() const override { return "interrupt"; }
}; // class InterruptThreadException

   // Interruptible thread
class InterruptibleThread {
public:
static void InterruptionPoint() noexcept(false) {
    if (!InterruptibleThread::_flag_ref) { return; }
    if (!InterruptibleThread::_flag_ref->load()) { return; }

    throw InterruptThreadException();
} // check_for_interrupt

template <typename Function>
InterruptibleThread(Function&& fun) :
    _thread([this, fun = std::move(std::forward<Function>(fun))]
{
    _flag_ref = _flag.get();
    fun();
})
{}

InterruptibleThread(InterruptibleThread&&) = default;
InterruptibleThread(const InterruptibleThread&) = delete;

bool Interrupting() const { return _flag->load(); }

void Interrupt() { _flag->store(true); }

void Join()
{
    _thread.join();
}

bool TimedJoin(int msec)
{
    return (std::async([=]() {Join(); }).wait_for(std::chrono::milliseconds(msec)) != std::future_status::timeout);
}

bool Joinable()
{
    return _thread.joinable();
}

void Terminate()
{
    TerminateThread(_thread.native_handle(), -1);
}

~InterruptibleThread()
{
    if (_flag.get() != nullptr)
    {
        *_flag = false;
        Interrupt();
    }
    if (_thread.joinable())
        _thread.join()
}

private:
    static thread_local std::atomic_bool* _flag_ref;
    std::unique_ptr<std::atomic_bool> _flag = std::make_unique<std::atomic_bool>();
    std::thread _thread;
};

And example of use: 以及使用示例:

std::vector<InterruptibleThread> grp;
for (auto it : _sourceImages)
    grp.emplace_back([this, it] {
    it->StartFrameProcessing();
    it->SetImageDelay(const_cast<EngineConfig*>(GetConfig())->ImageDelay);
});

You could modernize your code and pass a lambda into InterruptibleThread instead of passing a function and it's arguments (ie bind-style). 您可以现代化代码并将lambda传递给InterruptibleThread而不是传递函数及其参数(即绑定样式)。

#include <atomic>
#include <iostream>
#include <thread>
#include <vector>

struct InterruptibleThread
{
    std::thread _thread;

    template <typename Function>
    InterruptibleThread(Function&& fun)
        : _thread(std::forward<Function>(fun))
    {
    }
};

struct Test
{
    std::vector<InterruptibleThread> grp;
    void test(int x) {
        grp.emplace_back([this, x]{ t1(x); }); // <==== HERE
    }
    void t1(int x) {
        std::cout << x << "\n";
    }
};

int main()
{
    Test t;
    t.test(5);
    t.grp[0]._thread.join();
}

My guess would be that the immediate problem is that the vector class is trying to instantiate the move constructor of your class, which happens to match the template signature but then fails to compile the body with Function = InterruptibleThread, Args = {} . 我的猜测是直接的问题是向量类正在尝试实例化类的move构造函数,该构造函数恰好匹配模板签名,但随后无法使用Function = InterruptibleThread, Args = {}来编译主体。 You'll need to provide an override by explicitly defaulting the move constructor. 您需要通过显式默认移动构造函数来提供覆盖。

Aside from this, a couple other points: 除此之外,还有其他几点:

  • As written, the constructor shouldn't work with pointer-to-member-functions and std::reference_wrapper arguments. 如所写,构造函数不应使用指向成员函数的指针和std::reference_wrapper参数。 (It didn't work under gcc, and I don't see how it could have worked in MSVC.) (它在gcc下不起作用,我不知道它在MSVC中如何工作。)
  • It's not in the code sample, but it looks like _flag is a member variable of type atomic_bool . 它不在代码示例中,但看起来_flag是atomic_bool类型的成员变量。 That would prevent a default move constructor from being able to be instantiated. 这将阻止默认的move构造函数能够被实例化。

Here's a revised version which compiles for me under gcc: 这是在gcc下为我编译的修订版本:

#include <thread>
#include <mutex>
#include <atomic>
#include <vector>
#include <utility>

thread_local std::atomic_bool* _flag_ref;

class InterruptibleThread {
private:
  std::thread _thread;

  // Need unique_ptr instead of a directly contained atomic_bool
  // to make the object MoveConstructible.
  std::unique_ptr<std::atomic_bool> _flag;

public:
  template <typename Function, typename... Args>
  explicit InterruptibleThread(Function&& fun, Args&&... args)
  {
    _flag = std::make_unique<std::atomic_bool>();

    // Use std::bind to take care of all the details of
    // calling pointer-to-member-function or pointer-to-member-variable,
    // unwrapping std::reference_wrapper arguments, etc.
    auto bound_fun = std::bind(std::forward<Function>(fun), std::forward<Args>(args)...);

    _thread = std::thread([this, bound_fun = std::move(bound_fun)]
    {
        _flag_ref = _flag.get();
        bound_fun();
    }
        );
  }

  InterruptibleThread(InterruptibleThread&&) = default;
  InterruptibleThread(const InterruptibleThread&) = delete;
};

class Foo {
public:
  void func(int& n);
};

void test_func() {
  std::vector<InterruptibleThread> v;
  Foo f;
  int n = 5;
  v.emplace_back(&Foo::func, &f, std::ref(n));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM