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
将指向它们的指针转换为其第一个成员的指针。IIRCreinterpret\u cast
可能返回与用作输入不同的地址(由于对齐限制).标准布局类型保证您可以使用reinterpret\u cast
将指向它们的指针转换为其第一个成员的指针。IIRCreinterpret\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个初始值设定项的聚合要求)。不过,我同意正式允许的尾随填充