variant-lite icon indicating copy to clipboard operation
variant-lite copied to clipboard

Argument to nonstd::visit is always a const reference?

Open AndrewGaspar opened this issue 3 years ago • 8 comments

Try as I might, I cannot use nonstd::visit on C++14 to mutate the item currently stored in a variant. If I change to -std=c++17 (and therefore use std::variant), it works.

Very simple example: https://godbolt.org/z/nsYqxs

AndrewGaspar avatar Dec 02 '20 23:12 AndrewGaspar

Yes, the overview page explicitly lists all arguments to visit to be const lvalue references. That's indeed different from std::visit which takes its arguments as universal references.

The reason your tests work for C++17 is because when using C++17 nonstd::variant is a typedef for std::variant. You can disable that by defining -Dvariant_CONFIG_SELECT_VARIANT=variant_VARIANT_NONSTD during compilation.

AnthonyVH avatar Dec 03 '20 17:12 AnthonyVH

What limitation in pre-C++17 stops visit from taking the arguments as non-const lvalue references?

AndrewGaspar avatar Dec 03 '20 17:12 AndrewGaspar

I'm not really qualified to answer this, since I didn't take a good look at how visit is implemented here.

But my guess is that the code currently implements visit using const lvalue ref, since that makes it compatible with C++98. With a couple of extra ifdefs to select between C++98 or newer, I think implementing visit using universal references for C++11 and up should be possible. But I might be mistaken of course.

AnthonyVH avatar Dec 03 '20 19:12 AnthonyVH

Oh, I suppose even with C++11, using just plain lvalue references would prevent you from using visit with temporaries, since temporaries can't be passed as a non-const reference... I suppose you have to use universal references to get it to work right.

AndrewGaspar avatar Dec 03 '20 19:12 AndrewGaspar

I imagine I'm having the same problem, but the following doesn't work for C++ < 17:

#include <cstdlib>
#include <vector>
#include <queue>
#include <nonstd/variant.hpp>

using namespace std;

struct helper_size
{
    template<typename T>
    size_t operator()(const T& item)
    {
        return item.size();
    }
};

/*
 * 
 */
int main(int argc, char** argv) 
{
    using buffer_t = nonstd::variant<std::vector<char>,
                                     std::queue<char>>;
    
    buffer_t buf;
    size_t size = nonstd::visit(helper_size{}, buf);
    return 0;
}

For C++ >= 17, it's fine.

@martinmoene Is this the desired behaviour?

pfeatherstone avatar Apr 30 '21 08:04 pfeatherstone

If someone wants to write a PR at some point, abseil has an implementation too for reference.

pfeatherstone avatar Apr 30 '21 08:04 pfeatherstone

@pfeatherstone, Making operator()(const T& item) a const method lets it compile.

At the moment I'm quite removed from the internal workings of nonstd::visit and in how far the limitations are more or less easily overcome (or to split implementation for C++98, and C++11 and later).

struct helper_size
{
    template<typename T>
    size_t operator()(const T& item) const
    {
        return item.size();
    }
};

martinmoene avatar May 01 '21 12:05 martinmoene

Cheers

pfeatherstone avatar May 01 '21 14:05 pfeatherstone