Получить результат в вызывающей стороне от функции, возвращающей std :: optional из std :: vector

Я пытаюсь манипулировать std::optional, используя контейнер, например std::vector.

Я начал с кода ниже:

#include <iostream>
#include <vector>
#include <string>
#include <optional>

using namespace std;

using optional_vecs = std::optional<std::vector<std::string>>;

optional_vecs returnStrings()
{
    optional_vecs myVect(std::in_place); 
    myVect->emplace_back("Name");
    return myVect;
}

int main()
{
    for (auto e : returnStrings().value())
        std::cout << e << " ";

    return 0;
}

Проблема здесь в том, что я ничего не получаю на выходе: я предполагаю, что из-за того, что std::optional::value возвращает ссылку, поскольку в моем случае это ссылка на временный объект.

Итак, чтобы решить эту проблему, я попытался использовать std::reference_wrapper, как показано ниже:

using optional_vecs = std::optional<std::reference_wrapper<std::vector<std::string>>>;

optional_vecs returnStrings()
{
    optional_vecs myVect; 
    myVect->get().emplace_back("Name");
    return myVect;
}

Теперь у меня вылет и ошибка:

  • сбой происходит при попытке добавить строку «имя».
  • ошибка возникает, когда я пытаюсь использовать цикл for-range, говоря the range for loop requires a suitable "begin" function and none was found.

Приведенный ниже код работает, но мне не нравится объявлять переменную, а затем вызывать Value ():

int main()
{
    auto result = returnStrings();
    for (auto e : result.value())
        std::cout << e << " ";

    return 0;
}

Итак, как я мог вернуть std :: optional, содержащий std::vector в пути functionName().Value().


person Blood-HaZaRd    schedule 21.01.2020    source источник
comment
В чем смысл необязательного вектора? Почему бы просто не вернуть пустой вектор? Что ж, если пустой вектор означает что-то иное, чем отсутствие вектора ... но это все равно довольно бессмысленно.   -  person ALX23z    schedule 22.01.2020


Ответы (3)


Ваша проблема здесь в первых двух случаях заключается в том, что, поскольку returnStrings() возвращает временное значение, цикл for не продлит его жизнь, если вы действительно не захватите то, что он возвращает. Захват result.value() не принесет вам никакой пользы, поскольку он не продлит срок службы returnStrings().

Итак, как я мог вернуть std :: optional, содержащий std :: vector в виде functionName().Value().

Вы должны запечатлеть возвращение functionName(). Вы можете делать то же самое, что и вы, или в C ++ 20 вы можете использовать новую версию ranged init-statement, которая была создана для таких случаев и будет выглядеть так:

for (auto&& opt_vec =  returnStrings(); auto e : opt_vec.value())
    std::cout << e << " ";
person NathanOliver    schedule 21.01.2020

К сожалению, вам придется использовать последнюю конструкцию.

Объект optional отвечает за владение vector. C ++ не будет рекурсивно продлевать время жизни объекта, владеющего указанным объектом, поэтому, если этот объект-владелец будет уничтожен (что и будет, поскольку он временный), указанный объект также будет уничтожен.

Однако я отмечу одну вещь: по крайней мере, что касается GCC, это действительный код:

int main()
{
    for (auto ret = returnStrings(); auto e : ret.value())
        std::cout << e << " ";

    return 0;
}

Изменение на optional<reference_wrapper<vector>> также не работает, потому что исходная функция returnStrings возвращает rvalue, что означает, что если бы не копирование, этот исходный объект был бы назначен перемещением, а затем также был бы уничтожен.

Поэтому, если жизненно важно, чтобы функция возвращала optional<vector>, в вашем цикле for должно быть что-то, что правильно инициализирует сам необязательный объект.

person Xirema    schedule 21.01.2020

Вам нужно работать с std::optional оболочкой, когда вы работаете с ее базовым типом шаблона. В вашем случае это std::string, это должно сработать.

#include <iostream>
#include <vector>
#include <string>
#include <optional>

using namespace std;

using optional_vecs = std::optional<std::vector<std::string>>;

optional_vecs returnStrings()
{
    std::vector<std::string> myVect{};
    myVect.emplace_back("Name");
    return std::optional{ myVect };
}

int main()
{
    auto stringsOpt = returnStrings();

    if (stringsOpt) {
        for (auto& e : *stringsOpt)
            std::cout << e << " ";
    }

    return 0;
}

Этот код действительно работает на GCC.

P.S. : удивительно, но ваш исходный код не компилируется на MSVC.

person Ladence    schedule 21.01.2020