避免在标准容器中默认构造元素
Avoiding default construction of elements in standard containers
我有兴趣构建一个uninitialized_vector
容器,它在语义上与std::vector
相同,但需要注意的是,原本使用无参数构造函数创建的新元素将在不初始化的情况下创建。我主要感兴趣的是避免将POD初始化为0据我所知,没有办法通过将std::vector
与一种特殊的分配器相结合来实现这一点
我想以与std::stack
相同的方式构建我的容器,它适应用户提供的容器(在我的情况下,是std::vector
(。换句话说,我希望避免重新实现整个std::vector
,而是围绕它提供一个"门面">
有没有一种简单的方法可以从std::vector
的"外部"控制默认构造?
以下是我得到的解决方案,它受到了Kerrek答案的启发:
#include <iostream>
#include <vector>
#include <memory>
#include <algorithm>
#include <cassert>
// uninitialized_allocator adapts a given base allocator
// uninitialized_allocator's behavior is equivalent to the base
// except for its no-argument construct function, which is a no-op
template<typename T, typename BaseAllocator = std::allocator<T>>
struct uninitialized_allocator
: BaseAllocator::template rebind<T>::other
{
typedef typename BaseAllocator::template rebind<T>::other super_t;
template<typename U>
struct rebind
{
typedef uninitialized_allocator<U, BaseAllocator> other;
};
// XXX for testing purposes
typename super_t::pointer allocate(typename super_t::size_type n)
{
auto result = super_t::allocate(n);
// fill result with 13 so we can check afterwards that
// the result was not default-constructed
std::fill(result, result + n, 13);
return result;
}
// catch default-construction
void construct(T *p)
{
// no-op
}
// forward everything else with at least one argument to the base
template<typename Arg1, typename... Args>
void construct(T* p, Arg1 &&arg1, Args&&... args)
{
super_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
};
namespace std
{
// XXX specialize allocator_traits
// this shouldn't be necessary, but clang++ 2.7 + libc++ has trouble
// recognizing that uninitialized_allocator<T> has a well-formed
// construct function
template<typename T>
struct allocator_traits<uninitialized_allocator<T> >
: std::allocator_traits<std::allocator<T>>
{
typedef uninitialized_allocator<T> allocator_type;
// for testing purposes, forward allocate through
static typename allocator_type::pointer allocate(allocator_type &a, typename allocator_type::size_type n)
{
return a.allocate(n);
}
template<typename... Args>
static void construct(allocator_type &a, T* ptr, Args&&... args)
{
a.construct(ptr, std::forward<Args>(args)...);
};
};
}
// uninitialized_vector is implemented by adapting an allocator and
// inheriting from std::vector
// a template alias would be another possiblity
// XXX does not compile with clang++ 2.9
//template<typename T, typename BaseAllocator>
//using uninitialized_vector = std::vector<T, uninitialized_allocator<T,BaseAllocator>>;
template<typename T, typename BaseAllocator = std::allocator<T>>
struct uninitialized_vector
: std::vector<T, uninitialized_allocator<T,BaseAllocator>>
{};
int main()
{
uninitialized_vector<int> vec;
vec.resize(10);
// everything should be 13
assert(std::count(vec.begin(), vec.end(), 13) == vec.size());
// copy construction should be preserved
vec.push_back(7);
assert(7 == vec.back());
return 0;
}
该解决方案的工作取决于特定供应商的编译器&STL的std::vector
实现符合c++11。
与其在容器周围使用包装器,不如考虑在元素类型周围使用包装:
template <typename T>
struct uninitialized
{
uninitialized() { }
T value;
};
我认为问题归结为容器对元素执行的初始化类型。比较:
T * p1 = new T; // default-initalization
T * p2 = new T(); // value-initialization
标准容器的问题在于,它们采用默认参数进行值初始化,如resize(size_t, T = T())
中所示。这意味着没有优雅的方法可以避免值初始化或复制。(构造函数也是如此。(
即使使用标准分配器也不起作用,因为它们的中心construct()
函数接受一个已初始化值的参数。您更需要的是使用默认初始化的construct()
:
template <typename T>
void definit_construct(void * addr)
{
new (addr) T; // default-initialization
}
这样的东西将不再是一个符合标准的分配器,但您可以围绕这个想法构建自己的容器。
我不相信通过包装向量(适用于所有类型(来实现这一点,除非您在每次添加和删除操作上调整向量的大小。
如果您可以放弃包装STL容器,那么可以通过在堆上保留一个char
数组并对要构建的每个对象使用放置new
来实现这一点。通过这种方式,您可以精确地控制何时逐个调用对象的构造函数和析构函数。
- C++默认情况下,指针类型数组的元素是否保证初始化为 nullptr?
- std::map:当元素不可默认构造时创建/替换元素
- 如何在 c++ 中创建对的优先级队列.这会弹出具有最小值的元素.默认的弹出最大值
- std::tuple默认构造函数,带有move可构造元素
- 如果我想从类型"T"定义元素的容器(来自 STL),那么"T"必须使用默认构造函数?
- 为 unordered_map 中的元素设置默认构造函数(如果是 [] 运算符)
- 在 new 关键字中,由默认构造函数初始化的类中的元素是否也使用 new 关键字在C++?
- C2280 STD :: vector的默认构造函数尽管具有移动构造函数,但仍需要元素的复制构造函数
- 在 std::map 中插入非默认构造元素
- 如何在默认和二维数组中找到顺序扩大元素的位置
- 初始化非默认可构造元素的 std::数组
- 使用默认成员初始值设定项对 std::array 的元素进行零初始化
- 超过非默认可构造元素的最后一个元素
- 具有昂贵或已删除的默认构造函数的最小n个元素
- 为什么默认情况下使用 [ ] 运算符访问超出范围的矢量元素时,C++检测?
- 默认构造函数自动创建的数组元素?
- 没有默认构造函数的重复元素的模板化数组
- 如何设置或初始化表、2d数组或多元素数组的所有元素的默认值
- 为向量成员和更新元素指定默认值
- std::vector::resize默认构造新元素的次数