C++ std::阵列存储位置如何?

C++ std::阵列存储位置如何?,c++,arrays,c++11,C++,Arrays,C++11,我希望有一种机制,允许我将可变函数参数(所有参数都可以转换为某些特定的普通旧数据类型F)连接到适当大小(大小大于或等于参数大小之和)的原始数据存储中。我编写了以下代码: #include <iostream> #include <iterator> #include <new> #include <cstdlib> #include <cassert> #include <array> #include <tuple&

我希望有一种机制,允许我将可变函数参数(所有参数都可以转换为某些特定的普通旧数据类型
F
)连接到适当大小(大小大于或等于参数大小之和)的原始数据存储中。我编写了以下代码:

#include <iostream>
#include <iterator>
#include <new>
#include <cstdlib>
#include <cassert>
#include <array>
#include <tuple>

template< typename F >
struct repacker
{

    constexpr
    repacker(F * const _storage)
        : storage_(_storage)
    {
        static_assert(std::is_pod< F >::value, "Underlying type is not a POD type.");
    }

    F * const storage_;

    template< typename... P >
    auto operator () (P && ...params) const
    {
        constexpr auto N = sizeof...(P);
        using A = std::array< F, N >; // using A = F [N]; this eliminates the problem
        static_assert(sizeof(A) == sizeof(F) * N, "This compiler does not guarantee, that this code to be working.");
#ifndef _NDEBUG
        auto a =
#else
        std::ignore =
#endif
                new (storage_) A{F(params)...};
        assert(static_cast< void * >(a) == static_cast< void * >(a->data()));
        return N;
    }

};

int main()
{
    using F = double;
    constexpr auto N = 6;
    F * a = new F[N];
    {
        F x(1.0);
        F const y(2.0); 
        repacker< F > r(a);
        auto const M = r(x, y, 3.0, 4, 5.0f, 6.0L);
        assert(M == N);
    }
    std::copy(a, a + N, std::ostream_iterator< F const & >(std::cout, " "));
    std::cout << std::endl;
    delete [] a;
    return EXIT_SUCCESS;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
模板
结构重新打包
{
常量表达式
重新包装(F*常数存储)
:存储(存储)
{
static_assert(std::is_pod::value,“底层类型不是pod类型”);
}
F*常数存储;
模板
自动运算符()(P&…参数)常量
{
constexpr auto N=sizeof…(P);
使用A=std::array;//使用A=F[N];这就消除了问题
static_assert(sizeof(A)==sizeof(F)*N,“此编译器不保证此代码正常工作。”);
#ifndef\u NDEBUG
自动a=
#否则
忽略=
#恩迪夫
新(存储)A{F(参数)…};
断言(static_cast(a)=static_cast(a->data());
返回N;
}
};
int main()
{
使用F=double;
constexpr auto N=6;
F*a=新的F[N];
{
fx(1.0);
F常数(2.0);
重新包装r(a);
自动常数M=r(x,y,3.0,4,5.0f,6.0L);
断言(M==N);
}
std::copy(a,a+N,std::ostream_迭代器(std::cout,“”);
std::cout(&a)=static_cast(a.data());
断言对于所有编译器都是正确的。这是代码正常工作的必要条件


断言总是正确的吗?

标准布局类型保证您可以使用
reinterpret\u cast
将指向它们的指针转换为其第一个成员的指针。IIRC
reinterpret\u cast
可能返回与用作输入不同的地址(由于对齐限制).

标准布局类型保证您可以使用
reinterpret\u cast
将指向它们的指针转换为其第一个成员的指针。IIRC
reinterpret\u cast
可能返回与用作输入不同的地址(由于对齐限制)

这是代码工作的必要条件

不,它不是。好的,它是必要的,但它是不够的。这个(非常糟糕的)代码工作的另一个必要条件是:

sizeof(std::array<F, N>) == sizeof(F) * N;
这是代码工作的必要条件

不,它不是。好的,它是必要的,但它是不够的。这个(非常糟糕的)代码工作的另一个必要条件是:

sizeof(std::array<F, N>) == sizeof(F) * N;

@AndyProwl指出了一些非常重要的事情:

std::array
保证是C++11 8.5.1/1定义的聚合:

聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有用于非静态数据成员的大括号或等式初始化器(9.2),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),也没有虚拟函数(10.3)

让我们用C++11 9/7测试一下:

标准布局类是指:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员
[……]

违反这一点比人们想象的要容易:

struct violator { virtual ~violator () { } };

typedef ::std::array<violator, 2> violated;
struct-violator{virtual~violator(){};
违反了typedef::std::数组;
在这里,类型
将具有非标准布局类的数组类型的数据成员


因此,C++11 9.2/20中表达的保证(允许将指向类的指针重新解释为指向其第一个成员的指针,并且据我所知,这是标准中唯一一段可能使您的假设有效的内容)并非在所有情况下都适用于
::std::array
>,而yprowl指出了一些非常重要的事情:

std::array
保证是C++11 8.5.1/1定义的聚合:

聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有用于非静态数据成员的大括号或等式初始化器(9.2),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),也没有虚拟函数(10.3)

让我们用C++11 9/7测试一下:

标准布局类是指:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员
[……]

违反这一点比人们想象的要容易:

struct violator { virtual ~violator () { } };

typedef ::std::array<violator, 2> violated;
struct-violator{virtual~violator(){};
违反了typedef::std::数组;
在这里,类型
将具有非标准布局类的数组类型的数据成员


因此,C++11 9.2/20中所表达的保证(允许将指向类的指针重新解释为指向其第一个成员的指针,这是我所能看到的标准中可能使您的假设有效的唯一一段)并非在所有情况下都适用于
::std::array

,但是?@Dukales:该标准并不保证,无论该网站怎么说。@Dukales:不,它具有包含数组的聚合类的语义。允许类在任何成员之后添加填充,并且该标准不保证数组是唯一的成员o类的大小可能大于数组的大小。@Dukales:标准不能保证这一点。因此,现在您不是在谈论标准,而是在谈论特定实现的特定依赖于实现的行为。@MikeSeymour标准保证成员数组(或其其他相邻的等价物)的大小是唯一的非静态数据成员(通过不超过N个初始值设定项的聚合要求)。不过,我同意正式允许的尾随填充