pezpezpez
pezpezpez

Reputation: 694

boost variant copy semantics

I was wondering what the copy semantics of boost variants are. I've checked the source code and it's a bit baffling to me so I was wondering, in the example code, if my getVal(name) function makes a copy of the underlying vector when it's returned? If so, should I change it to be a reference (&) returned instead?

using Val = boost::variant<std::vector<int>, std::vector<std::string>>;

Val getVal(std::string& name) {
 return map[name];// where map is std::map<std::string, Val>
}

Upvotes: 3

Views: 1376

Answers (1)

sehe
sehe

Reputation: 393557

Yes, your getVal returns a copy of the whole vectors (including copies of all the element strings, e.g.).

Yes, returning a reference instead solves this.


Note you can also have a variant that stores a reference. In this case, returning it by "value" still has the same semantics as returning the reference:

using Ref = variant<std::vector<int>&, std::vector<std::string>&>;

Ref getVal(std::string& name) {
   return map[name]; // where map is std::map<std::string, Val>
}

Full sample with the necessary mechanics to convert from Ref to Val (and vice versa):

Live On Coliru

#include <boost/variant.hpp>
#include <map>
#include <vector>
#include <string>


using Val = boost::variant<std::vector<int>, std::vector<std::string>>;
using Ref = boost::variant<std::vector<int>&, std::vector<std::string>& >;

std::map<std::string, Val> map {
    { "first", std::vector<int> { 1,2,3,4 } },
    { "2nd",   std::vector<std::string> { "five", "six", "seven", "eight" } }
};

namespace { // detail
    template <typename T>
    struct implicit_convert : boost::static_visitor<T> {
        template <typename U> T operator()(U&& u) const { return std::forward<U>(u); }
    };
}

Ref getVal(std::string& name) {
    return boost::apply_visitor(implicit_convert<Ref>(), map[name]);
}

#include <iostream>

int main() {
    for (auto i : boost::get<std::vector<int>         >(map["first"])) std::cout << i << " ";
    for (auto i : boost::get<std::vector<std::string> >(map["2nd"]))   std::cout << i << " ";
}

Output:

1 2 3 4 five six seven eight 

Without any vectors being copied

Upvotes: 2

Related Questions