0

I am trying to manipulate the std::optional using container like std::vector.

I started by doing the code below :

#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;
}

The problem here is that I get nothing in the output: I guess that because std::optional::value return a reference as in my case it is a reference to a temporary.

So to resolve that I tried to use std::reference_wrapper as below :

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;
}

Now I got a crash and an error :

  • the crash happens when trying to add the string "name".
  • the error is when I try to use the for-range loop saying the range for loop requires a suitable "begin" function and none was found.

The code below works but I don't like to declare a variable and then calli the Value():

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

    return 0;
}

So how could I return an std::optional holding a std::vector in the way functionName().Value().

Christophe
  • 68,716
  • 7
  • 72
  • 138
Blood-HaZaRd
  • 2,049
  • 2
  • 20
  • 43
  • What's the point of optional vector? Why not simply return an empty vector? Well, if empty vector means something other than no vector... but it is still rather pointless. – ALX23z Jan 21 '20 at 22:10

3 Answers3

3

Your issue here in the first two cases is that since returnStrings() returns a temporary, the for loop will not extend its life unless you actually capture what it returns. Capturing result.value() doesn't do you any good since it will not extend the lifetime of returnStrings().

So how could I return an std::optional holding a std::vector in the way functionName().Value().

You have to capture the return of functionName(). You can do what you did, or in C++20 you can use the new init-statement version of ranged for which was built for cases like this and would look like

for (auto&& opt_vec =  returnStrings(); auto e : opt_vec.value())
    std::cout << e << " ";
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
2

Unfortunately, you have to use the latter construct.

The optional object is responsible for owning the vector. C++ won't recursively extend lifetimes to the object owning a referenced object, so if that owning object is destroyed (which it will be, because it's temporary), the referenced object will also be destroyed.

One thing I will point out though: at least as far as GCC is concerned, this is valid code:

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

    return 0;
}

Changing to optional<reference_wrapper<vector>> also doesn't work because the original returnStrings function is returning an rvalue, meaning if it weren't for copy elision, that original object would be move-assigned and then destructed as well.

So if it's vitally important that the function return an optional<vector>, your for-loop will need to have something that properly initializes the optional object itself.

Xirema
  • 19,889
  • 4
  • 32
  • 68
0

You need to work with std::optional wrapper as you work with it's template underlying type. In your case, it's std::string, that should work.

#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;
}

This code actually works on GCC.

P.S. : it's surprisingly, but your source code doesn't compile on MSVC.

Ladence
  • 244
  • 2
  • 9