вариант повышения с пользовательскими классами

Я пробовал boost-variant с пользовательскими классами. Я понял, что безопасным способом доступа к содержимому класса является использование boost::static_visitor. Вы знаете, почему приведенный ниже код не компилируется? Есть ли какие-либо требования к подписи/декларации boost::static_visitor для использования?

Я нашел этот вопрос Почему я не могу посетить этот пользовательский тип с boost::variant?, но я его не понял.

С Уважением

АФГ

#include <iostream>
#include <algorithm>
#include <boost/variant.hpp>

struct CA{};

struct ca_visitor : public boost::static_visitor<CA>
{
    const CA& operator()(const CA& obj ) const { return obj;}
};

struct CB{};

struct cb_visitor : public boost::static_visitor<CB>
{
    const CB& operator()(const CB& obj) const { return obj;}
};

int main(){   
    typedef  boost::variant< 
        CA  
        ,CB >  v_type;

    v_type v;
    const CA& a = boost::apply_visitor( ca_visitor(), v );
}

person Abruzzo Forte e Gentile    schedule 03.03.2013    source источник


Ответы (1)


Прежде всего, аргумент шаблона boost::static_visitor<> должен указывать тип, возвращаемый оператором вызова. В вашем случае оператор вызова ca_visitor возвращает CA const&, а не CA.

Но это не самая большая проблема. Самая большая проблема заключается в том, что у вас, похоже, неправильное представление о том, как должны работать variant<> и static_visitor<>.

Идея boost::variant<> заключается в том, что он может содержать значения любого из типов, указанных вами в списке аргументов шаблона. Вы не знаете, что это за тип, и поэтому предоставляете посетителю несколько перегруженных операторов вызова для обработки каждого возможного случая.

Поэтому, когда вы предоставляете посетителя, вам нужно убедиться, что он имеет все необходимые перегрузки operator(), которые принимают типы, которые может содержать ваш variant. Если вы этого не сделаете, Boost.Variant вызовет ошибку компиляции (и сделает вам одолжение, потому что вы забыли обработать некоторые случаи).

Это проблема, с которой вы столкнулись: у вашего посетителя нет оператора вызова, принимающего объект типа CB.

Это пример правильного использования boost::variant<> и static_visitor<>:

#include <iostream>
#include <algorithm>
#include <boost/variant.hpp>

struct A{};
struct B{};

struct my_visitor : public boost::static_visitor<bool>
//                                               ^^^^
//                                               This must be the same as the
//                                               return type of your call 
//                                               operators
{
    bool operator() (const A& obj ) const { return true; }
    bool operator() (const B& obj) const { return false; }
};

int main()
{
    A a;
    B b;
    my_visitor mv;

    typedef boost::variant<A, B> v_type;

    v_type v = a;

    bool res = v.apply_visitor(mv);
    std::cout << res; // Should print 1

    v = b;

    res = v.apply_visitor(mv);
    std::cout << res; // Should print 0
}
person Andy Prowl    schedule 03.03.2013
comment
Тип посещения по типу может быть реализован через T* v = get<T>(var);, где v равно nullptr, если var не относится к типу T. Обратите внимание, что это хрупкое. - person Yakk - Adam Nevraumont; 03.03.2013
comment
@Yakk: Действительно, но мне кажется, что ОП спрашивал, как правильно использовать посетителя. - person Andy Prowl; 03.03.2013
comment
Конечно, но я полагаю, что показать им, как ходить в гости так, как они ожидали, может быть полезно: немногие из тех, кто не знает ответа, знают, о чем спрашивать. - person Yakk - Adam Nevraumont; 03.03.2013
comment
@Andy/@Yakk: Спасибо за ответ. Читая ваши объяснения, я тоже начал думать о значении варианта и его посетителе. Мне кажется, что это больше похоже на класс с «ограничением», состоящим в том, чтобы содержать по одному члену каждого возможного типа и static_visitor в качестве геттеров, проверяемых во время компиляции. Это слишком узкое мышление? BOOST представляет его как C-Union, но его члены используют одно и то же пространство памяти ... так в каком домене или контексте вы и Yakk используете варианты? (возможно, это другой вопрос на форуме) - person Abruzzo Forte e Gentile; 03.03.2013
comment
@AbruzzoForteeGentile: я думаю, вы более или менее поняли. Обычно я использую варианты, когда мне нужно добиться неинтрузивного полиморфизма во время компиляции, то есть когда я не хочу или не могу использовать виртуальные функции для дифференциации поведения алгоритма на основе типа объекта во время выполнения, над которым он работает. - person Andy Prowl; 03.03.2013