gpt4 book ai didi

c++ - emplace_back 初始化列表错误,当初始化列表对独立变量起作用时

转载 作者:行者123 更新时间:2023-11-28 04:03:07 26 4
gpt4 key购买 nike

所以我一直在制作一个对象池类,它是这样使用的:

class MagicTrick {
public:
MagicTrick(int magic) : _magic(magic)
{}
int predict() {
return _number * _magic;
}
private:
int _magic;
int _number = 2;
};
const std::size_t poolSize = 1;
ObjectPool<MagicTrick> magicTrickPool(poolSize, 5);

const int number = magicTrickPool.schedule([](MagicTrick& magicTrick){
return magicTrick.predict();
});

这很好用,但是当线程池使用的对象删除了它的复制构造函数时,例如数据成员是 std::unique_ptr 池的构造失败。在内部我使用一个 vector 来存储池:

struct ObjectAndLock {
Object object;
bool free;
static bool isFree(const ObjectAndLock& objectAndLock) {
return objectAndLock.free;
}
};
std::vector<ObjectAndLock> objectAndLocks;

然后我构建了完整的池类:

template<typename Object> 
class ObjectPool {
template<typename ...Args>
ObjectPool(std::size_t poolSize, Args&&... objectArgs)
: objectAndLocks(poolSize, { {std::forward<Args>(objectArgs)...}, true})
{}

这将构造带有此处列出的第三个重载的 vector https://en.cppreference.com/w/cpp/container/vector/vector

然而,这会将元素复制到 vector 中。所以我将其更改为 emplace_back 以在 vector 中就地构造对象,如下所示:

template<typename Object> 
class ObjectPool {
template<typename ...Args>
ObjectPool(std::size_t poolSize, Args&&... objectArgs)
{
if(poolSize == 0){
throw std::runtime_error("poolSize must be greater than 0");
}
objectAndLocks.reserve(poolSize);
for (std::size_t i = 0; i < poolSize; i++)
{
objectAndLocks.emplace_back({Object{std::forward<Args>(objectArgs)...}, true});
}
}
}

然而,这会出错:

Projects\ObjectPool\public_include\ObjectPool\ObjectPool.hpp(87): error C2660: 'std::vector<object_pool::ObjectPool<MagicTrick>::ObjectAndLock,std::allocator<_Ty>>::emplace_back': function does not take 1 arguments
with
[
_Ty=object_pool::ObjectPool<MagicTrick>::ObjectAndLock
]
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.23.28105\include\vector(651): note: see declaration of 'std::vector<object_pool::ObjectPool<MagicTrick>::ObjectAndLock,std::allocator<_Ty>>::emplace_back'
with
[
_Ty=object_pool::ObjectPool<MagicTrick>::ObjectAndLock
]

但是我可以像这样使用初始化列表在构造函数中创建一个对象,这样编译很好。

ObjectAndLock hello = { Object{std::forward<Args>(objectArgs)...}, true };

我看到了这个答案,但是我无法让它工作: emplace_back not working with std::vector<std::map<int, int>>我将 std::initializer_list 的模板用作:

std::initializer_list<ObjectAndLock>

也许这是错误的?

所以我的问题是如何让 emplace_back 正常工作?我最多可以使用 c++17

这是一个失败的示例类,因为它是不可复制的:

struct NonCopyable {
std::unique_ptr<int> number = std::make_unique<int>(10);
NonCopyable(const NonCopyable& other) = delete;
NonCopyable& operator=(const NonCopyable& other) = delete;
};

为了完整起见,这里是完整的类:

#ifndef OBJECTPOOL_H
#define OBJECTPOOL_H
#include <vector>
#include <functional>
#include <map>
#include <mutex>
#include <condition_variable>
#include <type_traits>
#include <algorithm>
#include <stdexcept>
#include <exception>
namespace object_pool {
namespace internal {
template <typename Function>
class DeferToDestruction {
Function _function;
public:
DeferToDestruction(Function function) : _function(function) {}
~DeferToDestruction() { _function(); }
};
}
template<typename Object>
class ObjectPool {
public:
/*!
@brief Create an object pool for

@param poolSize - Size of object pool, this must be atleast 1
@param objectArgs... - Arguments to construct the objects in the pool

Complete Example:
@code
class MagicTrick {
public:
MagicTrick(int magic) : _magic(magic)
{}
int predict() {
return _number * _magic;
}
private:
int _magic;
int _number = 2;
};

std::size_t poolSize = 5;
object_pool::ObjectPool<MagicTrick> magicTrickPool(poolSize, 5);

const int number = magicTrickPool.schedule([](MagicTrick& magicTrick){
return magicTrick.predict();
});
@endcode

Zero Argument Constructor Example:
@code
struct ZeroArgs {
int number = 2;
};

object_pool::ObjectPool<ZeroArgs> zeroArgsPool(1);
@endcode

Multiple Argument Constructor Example:
@code
class MultiArgs {
public:
MultiArgs(std::string name, int age, bool alive) {
_number = name.size() + age + (alive ? 5 : -5);
}
int predict() {
return _number * 2;
}
private:
int _number = 2;
};

object_pool::ObjectPool<MultiArgs> multiArgsPool(1, "bob", 99, true);
@endcode
*/
template<typename ...Args>
ObjectPool(std::size_t poolSize, Args&&... objectArgs)
{
if(poolSize == 0){
throw std::runtime_error("poolSize must be greater than 0");
}
objectAndLocks.reserve(poolSize);
for (std::size_t i = 0; i < poolSize; i++)
{
objectAndLocks.emplace_back({Object{std::forward<Args>(objectArgs)...}, true});
}
}

~ObjectPool(){

std::unique_lock<std::mutex> lock(objectAndLocksMutex);
const auto allobjectAndLocksFree = [this]() {
return std::all_of(std::begin(objectAndLocks), std::end(objectAndLocks), ObjectAndLock::isFree);
};
if(allobjectAndLocksFree()) {
return;
}
conditionVariable.wait(lock, allobjectAndLocksFree);
}

/*!
@brief Schedule access to the pool

@param callback - An callable with the the argument being a reference to the class stored in the object pool.

@return Returns return from the callback function, including void

Simple Example:
@code
const int number = magicTrickPool.schedule([](MagicTrick& magicTrick){
return magicTrick.predict();
});
@endcode
*/
template<typename FunctionWithObjectAsParameter>
auto schedule(FunctionWithObjectAsParameter&& callback)
{
const auto findFreeObject = [this]() {
return std::find_if(std::begin(objectAndLocks), std::end(objectAndLocks), ObjectAndLock::isFree);
};

std::unique_lock<std::mutex> lock(objectAndLocksMutex);
auto freeObject = findFreeObject();
if(freeObject == std::end(objectAndLocks)) {
conditionVariable.wait(lock, [this, &freeObject, &findFreeObject]{
freeObject = findFreeObject();
return freeObject != std::end(objectAndLocks);
});
}
freeObject->free = false;
lock.unlock();
internal::DeferToDestruction freeAndUnlockAndNotify([this, &freeObject] () {
{
std::scoped_lock<std::mutex> lock(objectAndLocksMutex);
freeObject->free = true;
}
conditionVariable.notify_one();
});
return callback(freeObject->object);
}
private:
struct ObjectAndLock {
Object object;
bool free;
static bool isFree(const ObjectAndLock& objectAndLock) {
return objectAndLock.free;
}
};
std::vector<ObjectAndLock> objectAndLocks;
std::mutex objectAndLocksMutex;
std::condition_variable conditionVariable;
};
}
#endif

最佳答案

如果您查看 emplace_back 的签名

template <class... Args>
reference emplace_back(Args&&... args);

你会发现 emplace_back 的参数类型是从你传递的参数推导出来的。 braced-init-list 可用于为特定类型的参数初始化参数。但是 {...} 本身没有类型,因此不能用于推断参数的类型。

emplace_back 所做的只是 std::forward您将传递给它的任何参数传递给元素类型的构造函数以在 vector 中就地创建元素。问题是你的

struct ObjectAndLock {
Object object;
bool free;
static bool isFree(const ObjectAndLock& objectAndLock) {
return objectAndLock.free;
}
};

甚至没有接受参数的构造函数(隐式复制和 move 构造函数除外)。

你要做的是

objectAndLocks.emplace_back(ObjectAndLock{Object{std::forward<Args>(objectArgs)...}, true});

即,为 emplace_back 初始化一个正确类型的值,以转发给隐式 move 构造函数。但这在本质上与只是做同样的事情

objectAndLocks.push_back({Object{std::forward<Args>(objectArgs)...}, true});

braced-init-listpush_back 一起工作,因为 push_back

void push_back(const T& value);
void push_back(T&& value);

确实期望元素类型的值而不是一组转发引用,因此,{...} 将最终初始化适当类型的参数...

C++20 将通过 T(…) 为聚合引入直接初始化,这将使简单的编写成为可能

objectAndLocks.emplace_back(Object{std::forward<Args>(objectArgs)...}, true);

这里。在那之前,我建议在这种情况下只使用 push_back ......

你的

struct NonCopyable {
std::unique_ptr<int> number = std::make_unique<int>(10);
NonCopyable(const NonCopyable& other) = delete;
NonCopyable& operator=(const NonCopyable& other) = delete;
};

将不可复制但也不可 move 。您声明了一个复制构造函数,这意味着不会有隐式声明的 move 构造函数 [class.copy]/8.1 .隐式声明的 move 赋值运算符的故事几乎相同 [class.copy.assign]/4 .您根本无法将不可 move 的类型作为 std::vector 的元素类型。要使 NonCopyable 可 move ,您必须定义 move 构造函数和 move 赋值运算符:

struct NonCopyable {
std::unique_ptr<int> number = std::make_unique<int>(10);

NonCopyable(const NonCopyable&) = delete;
NonCopyable(NonCopyable&&) = default;
NonCopyable& operator=(const NonCopyable&) = delete;
NonCopyable& operator=(NonCopyable&&) = default;
};

关于c++ - emplace_back 初始化列表错误,当初始化列表对独立变量起作用时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59174554/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com