将可变参数函数模板参数存储到union向量中的最有效方法是什么?

时间:2016-07-14 16:15:02

标签: c++ c++11 vector variadic-templates

我有一个简单的联合,其构造函数将其参数存储在相关成员中:

union Data
{
    Data(int    i) : _i(i) { }
    Data(double d) : _d(d) { }
    Data(char   c) : _c(c) { }

    int    _i;
    double _d;
    char   _c;
};

然后我有一个可变参数构造函数,它接受这些类型的参数并使用模板递归将它们存储在union的向量中:

template<typename... Ts>
DataStore(Ts... ts)
{
    _data.reserve(sizeof...(ts));
    store(ts...);
}

template<typename T, typename... Ts>
void store(T t, Ts... ts)
{
    _data.push_back(t);
    store(ts...);
}

void store()
{
    // terminal condition
}

这将导致一系列匹配参数数量的vector::push_back次调用。

这是填充联合向量的最有效/最快的方法吗?

是否有任何技巧(可以特定于x86-64 / Linux)我可以用来加快速度?

工作示例:

#include <iostream>
#include <vector>

union Data
{
    Data(int    i) : _i(i) { }
    Data(double d) : _d(d) { }
    Data(char   c) : _c(c) { }

    int    _i;
    double _d;
    char   _c;
};

struct DataStore
{
    template<typename... Ts>
    DataStore(Ts... ts)
    {
        _data.reserve(sizeof...(ts));
        store(ts...);
    }

    template<typename T, typename... Ts>
    void store(T t, Ts... ts)
    {
        _data.push_back(t);
        store(ts...);
    }

    void store()
    {
        // terminal condition
    }

    std::vector<Data> _data;
};

int main()
{
    DataStore d(1, 2.3, 'c');

    std::cout << d._data.size() << '\n'
              << d._data[0]._i  << '\n'
              << d._data[1]._d  << '\n'
              << d._data[2]._c  << '\n';

    return 0;
}

3 个答案:

答案 0 :(得分:5)

您可以直接初始化_data

template<typename... Ts>
DataStore(Ts... ts) : _data{ts...}
{}

答案 1 :(得分:4)

由于您的评论表明您反对使用初始化列表的简单方法,您可以这样做:

template<typename... T>
DataStore(T&&... ts)
{
  _data.reserve(sizeof...(ts));
  char dummy[] = { (_data.emplace_back(ts), '0')... }; 
}

那仍然会创建一个数组,但它是一个char的数组,它可能会被优化掉,而且没有递归,你也不需要进行任何实例化。 store功能模板。

数组的每个元素的初始化程序将参数包中的一个对象插入向量(按从左到右的顺序)。在此处使用emplace_back可以将任何类型传递给DataStore构造函数,这样您就不会在插入对象之前创建Data个对象。这意味着没有制作副本:

DataStore d{ 1, '2', 3.0 };

(堆栈上只是一个未使用的char[3]数组。)

如果你决定使用initializer_list,你应该立即创建它,而不是在DataStore构造函数中创建它:

DataStore(std::initializer_list<Data> list) : _data(list) { }

现在您可以将任意数量的Data对象传递给DataStore构造函数,因此临时数组是在该构造函数之外创建的,然后直接传递给vector成员。使用它,DataStore d{ Data{1}, Data{'2'}, Data{3.0} }将构造一个包含三个对象的数组,然后将每个对象复制到向量中。

使用此构造函数,您无法DataStore{1, '2', 3.0},因为编译器无法从不同类型的braced-init-list创建initializer_list<Data>

答案 2 :(得分:1)

如何:

template<typename... Ts>
DataStore(Ts... ts)
{
    _data = {ts...};
}

这将使用emplace_back,因此您可以直接传递存储在union中的基本类型。