Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost::variant - Why a template parameter has higher precedence than a const string parameter

I am witnessing a behavior in the following code that I don't understand. The point is that if I declare the second overload of operator() like either of the following:

bool operator()(T other) const
bool operator()(const T &other) const

The output of program is:

string

But if I use the following declaration:

bool operator()(T &other) const

The output will be:

other type

Can someone please explain why operator()(const string &other) is not being called in the latter case?

#include "boost/variant/variant.hpp"
#include "boost/variant/apply_visitor.hpp"

using namespace std;
using namespace boost;

typedef variant<string, int> MyVariant;


class StartsWith
    : public boost::static_visitor<bool>
{
public:
    string mPrefix;
    bool operator()(const string &other) const
    {
        cout << "string" << endl;
        return other.compare(0, mPrefix.length(), mPrefix) == 0;
    }
    template<typename T>
    bool operator()(T &other) const
    {
        cout << "other type" << endl;
        return false;
    }
    StartsWith(string const& prefix):mPrefix(prefix){}
};

int main(int argc, char **argv) 
{
    MyVariant v(string("123456"));
    apply_visitor(StartsWith("123"), v);
    return 0;
}
like image 724
B Faley Avatar asked Nov 10 '12 07:11

B Faley


1 Answers

You have const problem here.

You are passing not const object to apply_visitor - so not const object members are passed to applied visitor. So in your case it is string& - reference to string type. This template is exact match for it:

template<typename T>
bool operator()(T &other) const

So it is selected. This function is not exact match - it is skipped:

bool operator()(const string &other) const

Of course if you provide that operator:

bool operator()(string &other) const

then it would be selected, since non template function are considered before template one.

So solution is: either provide method in your visitor which takes string reference (not const) - or pass const variant to apply...

First solution - remove const from string operator:

bool operator()(/*const*/ string &other) const
//              ^^^^^^^^^ remove it

Second solution - pass const object:

const MyVariant& cv = v;
apply_visitor(StartsWith("123"), cv);
//                               ^^ const object passed here

Third solution - add const specifier to general visitor:

template<typename T>
bool operator()(const T &other) const
//              ^^^^^ 

Solutions 1st and 3rd are the better than 2nd - you should pass consistent visitor to your variant, const has strong meaning when compiler has to select appropriate function.

like image 195
PiotrNycz Avatar answered Oct 08 '22 01:10

PiotrNycz