Функция возвращает кортеж из векторов

Я пытаюсь избежать выходных аргументов в моих функциях. Старая функция:

void getAllBlockMeanError(
    const vector<int> &vec, vector<int> &fact, vector<int> &mean, vector<int> &err)

Здесь vec - входной аргумент, fact, mean и err - выходной аргумент. Я попытался сгруппировать выходной аргумент в один кортеж:

tuple< vector<int>, vector<int>, vector<int> > 
                                  getAllBlockMeanErrorTuple(const vector<int> &vec)
{
    vector<int> fact, mean, err;
    //....
    return make_tuple(fact, mean, err);
}

Теперь я могу вызвать новую функцию с помощью:

tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec);

Это выглядит чище для меня. Хотя у меня есть вопрос, как работает равное присвоение tie(fact, mean, err)? Делает ли он глубокую копию или движение? Поскольку fact, mean и err внутри getAllBlockMeanErrorTuple будут уничтожены, я надеюсь, что он делает переход вместо глубокой копии.

Ответ 1

Вы используете подпись tuple< vector<int>, vector<int>, vector<int> >, которая является временной, и элементы могут быть перемещены, поэтому

std::tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec)

следует переместить-присваивать fact, mean и err.

Вот пример программы для вас, чтобы увидеть себя (demo):

#include <iostream>
#include <vector>
#include <tuple>

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

std::tuple<A, A> DoTheThing()
{
    A first;
    A second;
    return std::make_tuple(first, second);
}

int main()
{
    A first;
    A second;
    std::tie(first, second) = DoTheThing();
}

Вывод:

Скопировать ctor
Копировать ctor
Переместить назначение
Переместить назначение

Обратите внимание: функция должна была создать копии векторов для возврата tuple, которые могут быть не такими, какие вы хотите. Вы можете захотеть std::move элементов в std::make_tuple:

return make_tuple(std::move(fact), std::move(mean), std::move(err));

Здесь тот же пример, что и выше, но с std:: move, используемый в make_tuple

Обратите внимание, что с С++ 17 Structured Bindings вы можете вообще забыть об использовании std::tie и больше ориентироваться на auto (Спасибо, @Yakk):

auto[fact, mean, err] = getAllBlockMeanErrorTuple(vec);

Ранние реализации стандарта С++ 17 для clang (3.8.0) и gcc (6.1.0) пока не поддерживают его, однако, похоже, в clang 4.0.0 есть некоторая поддержка: Демо (Спасибо, @Revolver_Ocelot)

Вы заметите, что результат со структурированными привязками изменяется на:

Переместить ctor
Переместить ctor

Указывая на то, что они используют преимущества копирования, которые сохраняют дополнительные операции перемещения.

Ответ 2

std::tie(fact, mean, err) = getAllBlockMeanErrorTuple(vec);

выполнит назначение перемещения.

Но как упоминалось в комментарии

return make_tuple(fact, mean, err);

сделает копию, вы можете решить это с помощью:

return make_tuple(std::move(fact), std::move(mean), std::move(err));