将元素的向量转换为另一个元素的向量,而无需初始化后者

发布于 2025-02-05 17:55:16 字数 950 浏览 0 评论 0原文

我有一个const std :: vector< t>源大小的和std :: vector< t> dest。 我想将转换应用于source中的每个元素,然后将其存储在dest中; 所有这些并行,并且知道t不是默认的构造

我最初尝试的是使用并行执行策略使用std :: transform

std::transform(std::execution::par_unseq,
  source.begin(), source.end(),
  dest.begin(),
  [](const T& elem) { return op(elem); }
);

但是,当我第一次编译并运行此操作时,令我惊讶的是,我发现,尽管transform> transform“ loops” for source.size() times,dest的内容保持不变。

我发现这是因为dest必须在变换之前具有相同的source的大小。 但是,我无法将dest调整到source的大小,因为t不默认构造,因为它没有默认的构造函数。我也希望不为其提供默认的构造函数(首先,在t的逻辑中没有意义,但是您可以认为调用它会很昂贵)。

C ++ STL是否提供其他算法来实现我的想法?

足够的是算法,其中每个线程计算source向量的部分,然后收集结果并将其连接到相同的dest向量。

I have a const std::vector<T> source of a big size, and a std::vector<T> dest.
I want to apply a transformation to each element in source and store it in dest; all of this in parallel, and knowing that T is not default constructible.

What I initially attempted was using std::transform with a parallel execution policy:

std::transform(std::execution::par_unseq,
  source.begin(), source.end(),
  dest.begin(),
  [](const T& elem) { return op(elem); }
);

However, when I first compiled and run this, to my surprise, I discovered that, although the transform "loops" for source.size() times, dest's content remains unchanged.

I discovered that this is because dest must have the same size of source prior to the transform.
However, I cannot do resize dest to the size of source, because T is not default constructible as it does not have a default constructor. I also would prefer not to provide a default constructor for it (first of all, it does not make sense in T's logic, but you can think that it would be expensive to call).

Does C++ STL offer any other algorithm for achieving what I have in mind?

What would suffice is an algorithm where each thread computes its own part of the source vector, and then the results are collected and joined into the same dest vector.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

丑丑阿 2025-02-12 17:55:17

尝试使用std :: vector&lt;&gt; std ::可选&lt; t&gt;

#include <algorithm>
#include <execution>
#include <optional>
#include <vector>

struct T { // NOTE: T is NOT default-constructible!
    T(int x) : value(x) {}
    operator int() { return value; }
    int value;
};

T op(const T& in) {
    return in.value * 2;
}

#include <iostream>

int main() {
    const std::vector<T> source = {4, 5, 6, 7, 8, 9, 10, 11};
    std::vector<std::optional<T>> dest(source.size());

    std::transform(
        std::execution::par_unseq,
        source.begin(), source.end(),
        dest.begin(),
        [](const T& elem) { return op(elem); }
    );
    for (auto i : dest) {
        std::cout << *i << " ";
    }
    std::cout << std::endl;
}

示例输出:8 10 12 14 16 16 18 20 22 22

godbolt演示: https://godbolt.org/z/ecf955cneq

您的dest数组现在将每个元素包裹在std ::可选&lt;&gt;容器中,但我相信它否则可以提供您指定的语义。

Try using a std::vector<> of std::optional<T>:

#include <algorithm>
#include <execution>
#include <optional>
#include <vector>

struct T { // NOTE: T is NOT default-constructible!
    T(int x) : value(x) {}
    operator int() { return value; }
    int value;
};

T op(const T& in) {
    return in.value * 2;
}

#include <iostream>

int main() {
    const std::vector<T> source = {4, 5, 6, 7, 8, 9, 10, 11};
    std::vector<std::optional<T>> dest(source.size());

    std::transform(
        std::execution::par_unseq,
        source.begin(), source.end(),
        dest.begin(),
        [](const T& elem) { return op(elem); }
    );
    for (auto i : dest) {
        std::cout << *i << " ";
    }
    std::cout << std::endl;
}

Sample output: 8 10 12 14 16 18 20 22

Godbolt demo: https://godbolt.org/z/ecf95cneq

This is admittedly not that idiomatic, as your dest array now has every element wrapped up in an std::optional<> container, but I believe it otherwise offers the semantics you specify.

仲春光 2025-02-12 17:55:17

还受到 @saxbophone的答案的启发,这是一个解决方案,该解决方案避免了std ::可选介绍的(次要)开销。它基于以下简单的,您的自己的容器:

template <class T> class SimpleVector
{
public:
    SimpleVector (size_t capacity) :
        m_capacity (capacity), storage (static_cast <T *> (malloc (capacity * sizeof (T)))) { }
    ~SimpleVector () { free (storage); }

    T *data () { return storage; }
    T &operator [] (size_t i) { return storage [i]; }
    T *begin () { return storage; }
    T *end () { return storage + m_capacity; }

private:
    size_t m_capacity;
    T *storage;
};

这需要一个t要构造并随后移动由std :: transform生成的每个元素,与std ::可选解决方案相同(我确实对此进行了测试)。请注意使用malloc免费避免默认构建任何t s。

完整的代码为在这里,看起来像这样(它会产生与@sax相同的输出)

#include <iostream>
#include <algorithm>
#include <execution>
#include <vector>
#include <utility>

template <class T> class SimpleVector
{
public:
    SimpleVector (size_t capacity) :
        m_capacity (capacity), storage (static_cast <T *> (malloc (capacity * sizeof (T)))) { }
    ~SimpleVector () { free (storage); }

    T *data () { return storage; }
    T &operator [] (size_t i) { return storage [i]; }
    T *begin () { return storage; }
    T *end () { return storage + m_capacity; }

private:
    size_t m_capacity;
    T *storage;
};

struct T
{
    T (int x) : value (x) { }
    operator int () const { return value; }
    int value;
};

T op (const T& in) { return in.value * 2; }

int main ()
{
    const std::vector <T> source = {4, 5, 6, 7, 8, 9, 10, 11};
    SimpleVector <T> dest (source.size ());

    std::transform (std::execution::par_unseq,
        source.cbegin (), source.cend (), dest.begin (),
        [] (const T& elem) { return op (elem); } );

    for (const auto &it : dest) { std::cout << it << " "; }
}

:要使用SimpleVector作为您喜欢的任何内容。如果需要,您随时可以添加访问器 /便利方法。

Also inspired by @saxbophone's answer, here's a solution that avoids the (minor) overhead that std::optional introduces. It is based on the following simple, roll-your own container:

template <class T> class SimpleVector
{
public:
    SimpleVector (size_t capacity) :
        m_capacity (capacity), storage (static_cast <T *> (malloc (capacity * sizeof (T)))) { }
    ~SimpleVector () { free (storage); }

    T *data () { return storage; }
    T &operator [] (size_t i) { return storage [i]; }
    T *begin () { return storage; }
    T *end () { return storage + m_capacity; }

private:
    size_t m_capacity;
    T *storage;
};

This requires one T to be constructed and subsequently moved for each element generated by std::transform, which is the same as the std::optional solution (I did test this). Note the use of malloc and free to avoid default-constructing any Ts.

The complete code is here and looks like this (it generates the same output as @sax):

#include <iostream>
#include <algorithm>
#include <execution>
#include <vector>
#include <utility>

template <class T> class SimpleVector
{
public:
    SimpleVector (size_t capacity) :
        m_capacity (capacity), storage (static_cast <T *> (malloc (capacity * sizeof (T)))) { }
    ~SimpleVector () { free (storage); }

    T *data () { return storage; }
    T &operator [] (size_t i) { return storage [i]; }
    T *begin () { return storage; }
    T *end () { return storage + m_capacity; }

private:
    size_t m_capacity;
    T *storage;
};

struct T
{
    T (int x) : value (x) { }
    operator int () const { return value; }
    int value;
};

T op (const T& in) { return in.value * 2; }

int main ()
{
    const std::vector <T> source = {4, 5, 6, 7, 8, 9, 10, 11};
    SimpleVector <T> dest (source.size ());

    std::transform (std::execution::par_unseq,
        source.cbegin (), source.cend (), dest.begin (),
        [] (const T& elem) { return op (elem); } );

    for (const auto &it : dest) { std::cout << it << " "; }
}

Feel free to use SimpleVector for anything you like if you so choose. You can always add accessor / convenience methods to it if you need them.

淡墨 2025-02-12 17:55:17

受@saxbophone答案的启发,您还可以使用指针的向量。

将代码模板归功于@saxbophone。

#include <algorithm>
#include <execution>
#include <optional>
#include <vector>

struct T { // NOTE: T is NOT default-constructible!
    T(int x) : value(x) {}
    operator int() { return value; }
    int value;
};

T* op(const T& in) {
    return new T(in.value*2);
}

#include <iostream>

int main() {
    const std::vector<T> source = {4, 5, 6, 7, 8, 9, 10, 11};
    std::vector<T*> dest(source.size());

    std::transform(
        std::execution::par_unseq,
        source.begin(), source.end(),
        dest.begin(),
        [](const T& elem) { return op(elem); }
    );
    for (auto i : dest) {
        std::cout << *i << " ";
    }
    std::cout << std::endl;

    // Don't forget to delete dest.
}

实时示例: https://godbolt.org/z/r73qr1ze3

Inspired by @saxbophone answer, you could also use a vector of pointers.

Credit for the code template to @saxbophone.

#include <algorithm>
#include <execution>
#include <optional>
#include <vector>

struct T { // NOTE: T is NOT default-constructible!
    T(int x) : value(x) {}
    operator int() { return value; }
    int value;
};

T* op(const T& in) {
    return new T(in.value*2);
}

#include <iostream>

int main() {
    const std::vector<T> source = {4, 5, 6, 7, 8, 9, 10, 11};
    std::vector<T*> dest(source.size());

    std::transform(
        std::execution::par_unseq,
        source.begin(), source.end(),
        dest.begin(),
        [](const T& elem) { return op(elem); }
    );
    for (auto i : dest) {
        std::cout << *i << " ";
    }
    std::cout << std::endl;

    // Don't forget to delete dest.
}

Live example: https://godbolt.org/z/r73qr1ze3

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文