Возможно ли заполнить вектор элементами в его конструкторе с идеальной передачей?

Скажите, пожалуйста, возможно ли вызвать для объекта класса std::vector конструктор, который заполнит вектор заданным количеством элементов, каждый из которых конструируется не копированием, и не перемещением, а идеальной передачей параметров в конструктор конструируемых элементов?

Например:

class A
{
public:
  A(const int x, const int y) {}
};

class B
{
public:
  B(const int x, const int y)
    :
    A_{ x, y }
  {}
private:
  A A_;
};

int main()
{
    std::vector<B> vec(10, 10, 10);// ?
    return 0;
}

Ответы (2 шт):

Автор решения: KoVadim

У класса B - два параметра. А в main Вы пытаетесь туда передать три параметра. Что с этого ожидается - я не знаю. Но можно вот так

std::vector<B> vec{{10, 10}, {10,10} };

будет два объекта типа B векторе.

Но есть честный способ избавится от лишних конструкторов - emplace_back

std::vector<B> vec;
vec.reserve(10); // выделяем нужное кол-во памяти. Можно с запасом
vec.emplace_back(10,10); // и добавляем
vec.emplace_back(10,10);
vec.emplace_back(10,10);
vec.emplace_back(10,10);
vec.emplace_back(10,10);
→ Ссылка
Автор решения: HolyBlackCat

Нужно использовать конструктор вектора с двумя итераторами, и передать туда кастомный итератор, у которого operator* возвращает класс с перегруженным operator B.

За счет обязательного copy elision, должно получиться ноль копирований и перемещений.

Итератор должен быть random-access, чтобы вектор сразу знал сколько нужно элементов, и не перевыделял память в конструкторе.

Perfect forwarding аргументов в конструктор делать нельзя, чтобы он из них ничего не переместил.

Писал на коленке, но вроде работает:

#include <cstddef>
#include <iostream>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

template <typename T> struct tag {};

template <typename T, typename ...P>
class ConstructingIterator
{
    std::size_t count = 0;

    struct Proxy
    {
        std::tuple<std::remove_reference_t<P> *...> params;

        operator T() const
        {
            return std::apply([](auto *... params){return T(*params...);}, params);
        }
    };
    Proxy proxy;

  public:
    using value_type = Proxy;
    using pointer = const Proxy *;
    using reference = const Proxy &;
    using difference_type = std::ptrdiff_t;
    using iterator_category = std::random_access_iterator_tag;

    ConstructingIterator() {}
    ConstructingIterator(std::size_t count, tag<T>, P &&... params)
        : count(count), proxy{{&params...}}
    {}

    template <typename C>
    C to()
    {
        return C(*this, ConstructingIterator());
    }

    reference operator*() const
    {
        return proxy;
    }

    pointer operator->() const
    {
        return &proxy;
    }

    ConstructingIterator &operator++()
    {
        count--;
        return *this;
    }
    ConstructingIterator &operator--()
    {
        count++;
        return *this;
    }

    ConstructingIterator operator++(int)
    {
        ConstructingIterator ret;
        operator++();
        return ret;
    }
    ConstructingIterator operator--(int)
    {
        ConstructingIterator ret;
        operator--();
        return ret;
    }

    // We don't need to separately define `!=` starting from C++20.
    bool operator==(const ConstructingIterator &other) const
    {
        return count == other.count;
    }
    auto operator<=>(const ConstructingIterator &other) const
    {
        return other.count <=> count; // sic
    }

    ConstructingIterator &operator+=(difference_type d)
    {
        count -= d;
        return *this;
    }
    ConstructingIterator &operator-=(difference_type d)
    {
        count += d;
        return *this;
    }

    // We don't need to separately define `number +/- iterator` starting from C++20.
    ConstructingIterator operator+(difference_type d) const
    {
        ConstructingIterator ret(*this);
        ret += d;
        return ret;
    }
    ConstructingIterator operator-(difference_type d) const
    {
        ConstructingIterator ret(*this);
        ret -= d;
        return ret;
    }

    difference_type operator-(const ConstructingIterator &other) const
    {
        return other.count - count; // sic
    }

    reference operator[](difference_type d) const
    {
        return *(*this + d);
    }
};


struct A
{
    A() {std::cout << "A()\n";}
    A(const A &) {std::cout << "A(const A &)\n";}
    A(A &&) {std::cout << "A(A &&)\n";}
    A &operator=(const A &) {std::cout << "A &operator=(const A &)\n"; return *this;}
    A &operator=(A &&) {std::cout << "A &operator=(A &&)\n"; return *this;}
    ~A() {std::cout << "~A()\n";}

    int x = 0, y = 0;
    A(int x, int y) : x(x), y(y) {std::cout << "A(" << x << "," << y << ")\n";}
};

int main()
{
    std::vector<A> vec = ConstructingIterator(3, tag<A>{}, 10, 20).to<std::vector<A>>();
    for (const A &a : vec)
        std::cout << "[" << a.x << "," << a.y << "]\n";
}

запустить на gcc.godbolt.org

→ Ссылка