1

I want to have mechanism that allows me to concatenate variadic function parameters (all of them are convertable into value of some specific plain-old-data type F) into a raw data storage of appropriate size (size is greater than or equal to sum of parameters sizes). I wrote the following code:

#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;
}

But I am not sure that the assert(static_cast< void * >(&a) == static_cast< void * >(a.data())); assertion is true for all the compilers. This is a necessary condition for a code to be working.

Is it always the assertion is true?

4

3 回答 3

5

这是代码正常工作的必要条件。

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

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

标准并不能保证这一点。std::array与 C 样式数组的布局不兼容。的内容std::array,但不是完整类型本身。

如果你想在内存中初始化一些你在位块中分配的对象,你应该新建一个char[]数组,而不是F. 你应该分配这个:

char *a = new char[sizeof(std::array<F, N>)];
于 2013-05-17T10:45:16.533 回答
4

@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;

在这里,该类型violated将具有一个非标准布局类的数组类型的数据成员。

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

于 2013-05-17T11:17:22.817 回答
1

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

于 2013-05-17T10:42:48.820 回答