Список инициализаторов членов: инициализировать два члена из функции, возвращающей кортеж

Можно ли инициализировать несколько элементов в списке инициализаторов элементов из кортежа, полученного функцией?

Поскольку возврат нескольких значений через кортежи становится все более популярным, я надеюсь, что для этого есть решение. Я не вижу причин, кроме языковых ограничений, почему это было бы невозможно.


Это mcve для того, что у меня есть:

auto new_foo(std::size_t size) -> std::tuple<std::unique_ptr<char[]>, int*>
{
    auto buffer = std::make_unique<char[]>(size * sizeof(int) + 8);
    auto begin = static_cast<int*>(static_cast<void*>(buffer.get() + 4));
    return std::make_tuple(std::move(buffer), begin);
}

struct X {
    std::unique_ptr<char[]> buffer_{nullptr};
    int* begin_{nullptr};
    std::size_t size_{0};

    X(std::size_t size) : size_{size}
    {
        std::tie(buffer_, begin_) = new_foo(size);
    }
};

Можно ли это сделать ?:

    X(std::size_t size)
        : buffer_{ ??? },
          begin_{ ??? },
          size_{size}
    {
    }

Я просто не могу вызвать new_foo один раз для инициализации каждого члена (поскольку он возвращает другой кортеж при каждом вызове). Так

    X(std::size_t size)
        : buffer_{std:get<0>(new_foo(size)},
          begin_{std:get<1>(new_foo(size)},
          size_{size}
    {
    }

это невозможно (даже если это не так, многократный вызов для получения одного и того же результата менее оптимален)

Еще одно решение, о котором я подумал, заключалось в том, чтобы хранить элементы как кортеж. Я отказался от этого, поскольку мне нужно, чтобы два члена были правильно названы внутри класса и не были доступны с помощью get<0> и get<1>.

Еще один обходной путь - создать простую отдельную структуру для хранения двух членов. Таким образом, они будут иметь имена, но добавить еще один уровень квалификатора, и, возможно, мне придется создать для него копию ctor (из-за unique_ptr).


Как сообщалось, здесь C++1z будет иметь структурированные привязки (D0144R0), что сделает это возможным:

auto {x,y,z} = f();

Поскольку я не нашел полного документа, я не могу сказать, поможет ли это в контексте списка инициализаторов членов. Я подозреваю, что нет.


person bolov    schedule 11.02.2016    source источник


Ответы (1)


Определите другой (возможно, частный) конструктор, который принимает кортеж и делегирует ему.

  private:
    X(std::tuple<std::unique_ptr<char>, int*> t, std::size_t size)
            : buffer_{std::move(std:get<0>(t))},
              begin_{std:get<1>(t)},
              size_{size}
    { }

 public:
    X(std::size_t size) : X{new_foo(size), size}
    { }
person Jonathan Wakely    schedule 11.02.2016