emplace_back初始化列表错误,当初始化列表在独立变量上工作时
emplace_back initialisation list error, when initialisation list works on standalone variable
所以我一直在做一个对象池类,它是这样用的:
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
。 在内部,我使用一个向量来存储池:
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})
{}
这将使用此处列出的第三个重载构造向量 https://en.cppreference.com/w/cpp/container/vector/vector
但是,这会将元素复制到向量中。 所以我把它改成emplace_back
,在向量中构造对象,如下所示:
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});
}
}
}
但是,这会导致以下错误:
ProjectsObjectPoolpublic_includeObjectPoolObjectPool.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 Studio2019CommunityVCToolsMSVC14.23.28105includevector(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不适用于 std::vector<std::map><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
参数的类型是从您传递的参数中推导出来的。大括号初始化列表可用于初始化特定类型参数的参数。但是{…}
本身没有类型,因此不能用于推断参数的类型。
emplace_back
所做的只是std::forward
传递给它的任何参数传递给元素类型的构造函数,以在向量中就地创建元素。问题在于您的
struct ObjectAndLock {
Object object;
bool free;
static bool isFree(const ObjectAndLock& objectAndLock) {
return objectAndLock.free;
}
};
甚至没有接受参数的构造函数(隐式复制和移动构造函数除外(。
您所要做的就是
objectAndLocks.emplace_back(ObjectAndLock{Object{std::forward<Args>(objectArgs)...}, true});
即,初始化正确类型的值,以便emplace_back
转发到隐式移动构造函数。但这本质上与仅仅做一回事
objectAndLocks.push_back({Object{std::forward<Args>(objectArgs)...}, true});
大括号初始化列表适用于push_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;
};
将不可复制,但也不能移动。您声明了一个复制构造函数,这意味着不会有隐式声明的移动构造函数 [class.copy]/8.1。对于隐式声明的移动赋值运算符 [class.copy.assign]/4,情况几乎相同。您根本不能将不移动类型作为std::vector
的元素类型。要使NonCopyable
可移动,您必须定义一个移动构造函数和移动赋值运算符:
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;
};
- 复制列表初始化的隐式转换的等级是多少
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 我使用向量来创建类对象列表.初始化向量时如何使用参数调用构造函数?
- C++11 中的混合列表初始化
- 我可以列表初始化 std::vector 并完美转发元素吗?
- 无法在声明时使用初始值设定项列表初始化常量字符*/字符串数组的向量
- C++20 从括号中的值列表初始化聚合,不支持内部数组
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 默认参数和空列表初始化
- 如何在列表初始化中放置额外的语句?
- C++列表初始化允许多个用户定义的转换
- 列表初始化是否将原子初始化为零
- 使用可变模板列表初始化数组,并放置new
- 使用整数初始化列表初始化长双精度的向量
- 直接列表初始化的自动规则
- 使用初始化列表初始化unique_ptr的容器,继续
- 如何修复"非聚合无法使用初始值设定项列表初始化" <map>
- 直接列表初始化和复制列表初始化之间的差异
- 为什么我可以在不使用赋值运算符的情况下使用列表初始化普通数组
- C++ - 使用类中的初始值设定项列表初始化动态集