【问题标题】:Use of std::vector::emplace_back使用 std::vector::emplace_back
【发布时间】:2017-01-12 02:09:53
【问题描述】:

我有一些用于 std::thread 的类变形器。这是构造函数:

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)...
        );
}

然后我正在使用它(示例): InterruptibleThread(&SourceImageList::StartFrameProcessingStatic, this, std::ref(it))

编译器成功构建此代码。但现在我想制作一个此类对象的向量:

std::vector<InterruptibleThread> grp;

我想在堆栈上分配它,所以我正在做的是:

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

我收到了这个错误:

C2064   term does not evaluate to a function taking 0 arguments

以下是编译器验证的选项:

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

但第一个是在堆上分配一个对象,所以我需要手动释放它,第二个复制该对象。

我可以在这里使用emplace_back吗(编译器是MSVC 2015 update 3)?

更新

好的,我根据答案做了一些修复。这是这个类的最终版本:

#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;
};

以及使用示例:

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

【问题讨论】:

  • “我可以在这里使用 emplace_back” - 你尝试过吗?实际上只是尝试会回答您的问题。
  • 您提供的第二个push_back 版本实际上应该move InterruptibleThread 在推回向量而不是创建副本时 - 编译器几乎肯定会使用此重载。
  • @ArchbishopOfBanterbury 在 C++11 中它肯定会移动它(所以编译器不会做任何额外的优化),因为 push_back 有一个右值重载 en.cppreference.com/w/cpp/container/vector/push_back
  • @vsoftco 确实,我的意思是说“过载”而不是“优化”,现在编辑了我的评论。
  • @JesperJuhl,OP 做了尝试。正如代码 sn-p 和相关错误所示。

标签: c++ c++11 vector std


【解决方案1】:

您可以现代化您的代码并将 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();
}

【讨论】:

  • 不错。天才是简单。谢谢!
【解决方案2】:

我的猜测是,直接的问题是向量类正在尝试实例化您的类的移动构造函数,它恰好与模板签名匹配,但随后无法使用Function = InterruptibleThread, Args = {} 编译主体。您需要通过显式默认移动构造函数来提供覆盖。

除此之外,还有几点:

  • 正如所写,构造函数不应该与指向成员函数的指针和std::reference_wrapper 参数一起使用。 (它在 gcc 下不起作用,我看不出它在 MSVC 中如何工作。)
  • 它不在代码示例中,但看起来_flag 是atomic_bool 类型的成员变量。这将阻止默认移动构造函数被实例化。

这是一个在 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));
}

【讨论】:

  • 现在看来可以编译了。我也喜欢@RustyX 的解决方案,它可以让代码更简单。谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-01-15
  • 1970-01-01
  • 2017-09-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多