Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/153.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ gcc和clang STL实现是否违反了关于分配器的规则?_C++_Gcc_Clang - Fatal编程技术网

C++ gcc和clang STL实现是否违反了关于分配器的规则?

C++ gcc和clang STL实现是否违反了关于分配器的规则?,c++,gcc,clang,C++,Gcc,Clang,在23.2.1p3 C++11标准中,我们可以看到: 对于受本子条款影响的声明了分配器类型的组件,存储在这些组件中的对象应使用allocator\u traits::construct函数构造,并使用allocator\u traits::destroy函数(20.6.8.2)销毁。这些函数仅针对容器的元素类型调用,而不针对容器使用的内部类型调用。[注意:这意味着,例如,基于节点的容器可能需要构造包含对齐缓冲区的节点,并调用construct将元素放入缓冲区。-结束注意] allocator\

在23.2.1p3 C++11标准中,我们可以看到:

对于受本子条款影响的声明了
分配器类型的组件,存储在这些组件中的对象应使用
allocator\u traits::construct
函数构造,并使用
allocator\u traits::destroy
函数(20.6.8.2)销毁。这些函数仅针对容器的元素类型调用,而不针对容器使用的内部类型调用。[注意:这意味着,例如,基于节点的容器可能需要构造包含对齐缓冲区的节点,并调用
construct
将元素放入缓冲区。-结束注意]


allocator\u traits::construct
只要调用传递的分配器的
construct
方法,如果分配器定义了一个。我尝试使用它并创建分配器,它使用列表初始化进行构造,因此我可以利用
emplace
进行聚合初始化:

#include <memory>
#include <vector>
#include <string>
#include <iostream>
#include <cmath>

template<typename T>
struct init_list_allocator : public std::allocator<T> {
    template<typename... Args>
    void construct(T* p, Args&&... args)
        { ::new((void *)p) T{std::forward<Args>(args)...}; }

    // Fix copy-constructors usage for aggregates
    void construct(T* p, T& copy_construct_arg)
        { std::allocator<T>::construct(p, copy_construct_arg); }

    void construct(T* p, const T& copy_construct_arg)
        { std::allocator<T>::construct(p, copy_construct_arg); }

    void construct(T* p, const T&& copy_construct_arg)
        { std::allocator<T>::construct(p, std::move(copy_construct_arg)); }

    void construct(T *p, T&& move_construct_arg)
        { std::allocator<T>::construct(p, std::move(move_construct_arg)); }
};

template<class T>
using improved_vector = std::vector<T, init_list_allocator<T>>;

struct A {
    int x;
    double y;
    const char* z;
};

int main()
{
    using namespace std;
    vector<string> strings;
    improved_vector<A> v;
    for (int i = 0; i < 21; ++i) {
        strings.emplace_back(to_string(i*i));
        v.emplace_back(i, sqrt(i), strings.back().c_str());
    };
    for (const auto& elem : v)
        cout << elem.x << ' ' << elem.y << ' ' << elem.z << '\n';
}
在我们分配器的定义中,这段代码将起作用。很好,现在让我们将
向量
更改为
列表
。这里我们有一个无法解决的问题,因为对象不是在
Allocator::construct:
中初始化的,而是在
std::\u List\u节点中以直接初始化形式(带括号的形式)初始化的


这两个问题是标准冲突还是我遗漏了什么?

据我所知,libstdc++和MSVC++在这里是正确的。如注释所示,
rebind
的要点是,可能需要容器来构造不是
T
的东西。例如,
std::list
需要构造一个包含
T
的列表节点,而不是
T
。对于关联容器和无序容器也存在类似的情况。这就是为什么
rebind
结构首先存在的原因。在此之前,您的分配器是不合格的


关于第二个问题,请参考

这些函数仅针对容器的元素类型调用,而不针对容器使用的内部类型调用

但是,似乎表明标准库实现不允许为回弹分配器调用
构造
。这可能是libstdc++中的一个bug


至于这个问题的实际解决方案,给
A
一个具有您想要的行为的构造函数,而不必为此而使用分配器。人们可能希望使用特殊分配器在容器外部创建
的实例:

#include <vector>

struct A {
    int x;
    double y;
    const char* z;
    A() = default; // This allows A to still be a POD because the default constructor
                   // is not "user-provided", see 8.4.2 [dcl.fct.def.default]/4
    A(int x_, double y_, char const* z_) : x(x_), y(y_), z(z_) {}

};

int main()
{
    using namespace std;
    vector<string> strings;
    vector<A> v;
    for (int i = 0; i < 21; ++i) {
        strings.emplace_back(to_string(i*i));
        v.emplace_back(i, sqrt(i), strings.back().c_str());
    };
    for (const auto& elem : v)
        cout << elem.x << ' ' << elem.y << ' ' << elem.z << '\n';
}
#包括
结构A{
int x;
双y;
常数char*z;
A()=default;//这允许A仍然是POD,因为默认构造函数
//不是“用户提供的”,请参见8.4.2[dcl.fct.def.default]/4
A(int x_,double y_,char const*z_):x(x_),y(y_),z(z_){
};
int main()
{
使用名称空间std;
向量字符串;
向量v;
对于(int i=0;i<21;++i){
字符串。将字符串放回(到字符串(i*i));
v、 emplace_back(i,sqrt(i),strings.back().c_str());
};
用于(常量自动和元素:v)

cout经过一些研究,我自己找到了答案,并想提供它

对于第一个问题(使用
Allocator::rebind::other::construct
而不是
Allocator::construct
),我的第一个分配器实现(第二个正常)不满足
Allocator
部分的
rebind
要求,请参见17.6.3.5表28:

+------------------+-------------+-------------------------------------+ |表达式|返回类型|断言/注释前置/后置条件| +------------------+-------------+-------------------------------------+ |所有U(包括T)的类型名称| Y || |X::模板| | Y::模板重新绑定::另一个是X| |重新绑定:其他| || +------------------+-------------+-------------------------------------+ 关于第二个问题:GCC有旧的、在C++11之前实现的std::list,它只会在GCC 5.0中修复,因为此更改会破坏ABI(有关更多信息,请参阅)


然而,引用的标准要求,容器必须为准确地调用
分配器类型
而不是某些重新索引的类型调用
构造
函数,这似乎是一个标准缺陷().Libstdc++对
std::set
multiset
map
multimap
的实现依赖于这一事实,并对construct()使用重新绑定的分配器。

第一个问题显然不是冲突——嵌套的重新绑定结构的全部目的是允许容器分配“包装器”必要时。除了
vector
之外,几乎所有容器都必须这样做。我看不到标准中有任何地方禁止标准库实现在第二种情况下使用直接初始化,但我对此的第一次引用结果是不正确的,因此我现在删除了我的答案。对于第一个问题:我认为,在这一段中,标准要求容器应该使用
allocator\u type::construct
进行对象构造,其中每个容器的
allocator\u type
定义为
typedef allocator\u type
,其中
allocator
是容器的模板参数。因此可以使用
rebind
为了获得内存,但不是为了我们的对象构造。我错了吗?它可能是LIbSTDC++中的一个bug,是的。你引用的比特似乎表明了:除了:C++不是围绕需要对所有事物继承的设计而设计的。继承是一种非常紧密的耦合关系,通常,要正确使用继承,需要设计继承的类。h记住这一点,继承类需要对父类有很好的理解。从
std::allocator
继承不是实现分配器所必需的,并且该类不是parti
#include <vector>

struct A {
    int x;
    double y;
    const char* z;
    A() = default; // This allows A to still be a POD because the default constructor
                   // is not "user-provided", see 8.4.2 [dcl.fct.def.default]/4
    A(int x_, double y_, char const* z_) : x(x_), y(y_), z(z_) {}

};

int main()
{
    using namespace std;
    vector<string> strings;
    vector<A> v;
    for (int i = 0; i < 21; ++i) {
        strings.emplace_back(to_string(i*i));
        v.emplace_back(i, sqrt(i), strings.back().c_str());
    };
    for (const auto& elem : v)
        cout << elem.x << ' ' << elem.y << ' ' << elem.z << '\n';
}
+------------------+-------------+-------------------------------------+ | Expression | Return type | Assertion/note pre-/post- condition | +------------------+-------------+-------------------------------------+ | typename | Y | For all U (including T), | | X::template | | Y::template rebind<T>::other is X. | | rebind<U>::other | | | +------------------+-------------+-------------------------------------+