6

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;
}
jweyrich
  • 31,198
  • 5
  • 66
  • 97
B Faley
  • 17,120
  • 43
  • 133
  • 223

1 Answers1

6

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.

PiotrNycz
  • 23,099
  • 7
  • 66
  • 112
  • As a type freak, I would say that the **3rd** solution is **best**, as-in: **const-correct**. There is no reason to take the parameter by non-const reference if you are not going to modify it. – Matthieu M. Nov 10 '12 at 13:20
  • @MatthieuM. In this very case you are right. I did not mention `T&&` - probably it would be the best - but there are many SO posts discussing difference between `T&&` and `const T &` so I did not want to make this unnecessary mix here... – PiotrNycz Nov 10 '12 at 14:21