boost::variant и полиморфизм

Я хочу получить указатель на базовый класс из варианта повышения, если я поставлю исходный указатель на производный класс. Есть ли способ добиться этого. Следующий код не работает.

class A{ public: virtual ~A(){}}; class B : public A{};
typedef boost::variant<A*,B*> MyVar;
MyVar var = new B;
A* a = boost::get<A*> (var); // the following line throws exception

Может быть, у кого-то есть идея, как написать мою собственную функцию получения, которая будет проверять, является ли запрошенный тип базовым классом хранимого типа в варианте, а затем выполнять соответствующее приведение


person user152508    schedule 13.09.2014    source источник


Ответы (2)


Вы можете написать своего посетителя с помощью шаблона operator(), как показано ниже:

ЖИВАЯ ДЕМО

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

struct A { virtual ~A() {} virtual void foo() {} };
struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };

template <typename T>
struct visitor : boost::static_visitor<T>
{
private:
    using Base = typename std::remove_pointer<
                        typename std::remove_cv<
                            typename std::remove_reference<T>::type
                        >::type
                    >::type;

    template <typename U>
    T get(U& u, std::true_type) const
    {
        return u;
    }

    template <typename U>
    T get(U& u, std::false_type) const
    {
        throw boost::bad_get{};
    }

public:
    template <typename U>
    T operator()(U& u) const
    {
        using Derived = typename std::remove_pointer<
                            typename std::remove_cv<
                                typename std::remove_reference<U>::type
                            >::type
                        >::type;

        using tag = std::integral_constant<bool
                         , (std::is_base_of<Base, Derived>::value
                           || std::is_same<Base, Derived>::value)
                           && std::is_convertible<U, T>::value>;

        return get(u, tag{});
    }
};

template <typename T, typename... Args>
T my_get(boost::variant<Args...>& var)
{
    return boost::apply_visitor(visitor<T>{}, var);
}

int main()
{    
    boost::variant<A*,B*> var = new B;

    A* a = my_get<A*>(var); // works!
    a->foo();

    B* b = my_get<B*>(var); // works!
    b->foo();
}

Вывод:

B::foo()
B::foo()

Раздел вопросов и ответов:

Странное решение!

Нет это не так. Именно для этого предназначены классы посетителей в Boost.Variant. Аналогичное решение уже существует в последнем выпуске Boost.Variant: boost::polymorphic_get<T>. К сожалению, он был разработан для других целей и не может быть использован здесь.

person Piotr Skotnicki    schedule 13.09.2014
comment
Эта кнопка станет прецедентом. Жаль, что coliru не запускает большинство моих ответов (слишком тяжелые) - person sehe; 13.09.2014
comment
Один голос здесь - отметить количество людей в мире, которые думают, что это способ подойти к проблеме. В том числе ОП. - person SChepurin; 13.09.2014
comment
@SChepurin: не стесняйтесь представить свое решение - person Piotr Skotnicki; 13.09.2014
comment
@Piotr S. - Я не говорил, что твое решение плохое. Просто странное решение. - person SChepurin; 13.09.2014
comment
Было бы лучше использовать is_base_of и is_convertible во время компиляции, например. с enable_if. - person Igor R.; 13.09.2014
comment
@IgorR.: да, но текущее решение больше похоже на разновидность, что выдает исключение при несоответствии типов. - person Piotr Skotnicki; 13.09.2014
comment
Можно и с раствором CT кидать: в одной перегрузке просто return u;, в другой только throw std::runtime_error. Это устранит ветвление во время выполнения, а также лучше соответствует духу visitor. - person Igor R.; 13.09.2014
comment
@Пётр С. - Нет, это не так. Именно для этого и предназначены классы посетителей в Boost.Variant. Подобное решение уже есть в последней версии Boost.Variant — не переусердствуйте. Именно это я и имел в виду - Boost.Variant с духом посетителя - странная конструкция. - person SChepurin; 14.09.2014
comment
Всем привет. @Piotr S. спасибо за ваше решение. Это совсем не странно, однако мне удается его переработать и, возможно, получить более легкую и понятную версию, по крайней мере, для меня. Я опубликую это как ответ. - person user152508; 23.09.2014
comment
Здравствуйте, я хотел бы знать, каковы цели дизайна официального повышения polymorphic_get, если это не так? потому что это звучит потенциально очень вводящее в заблуждение. - person v.oddou; 10.04.2015
comment
@v.oddou Функция завершается успешно, только если содержимое имеет указанный тип U или тип, производный от типа U. Здесь OP хранит указатели в объекте варианта. Тип указателя никогда не является производным от какого-либо другого типа. - person Piotr Skotnicki; 10.04.2015

Привет, спасибо всем за ваши ответы и комментарии. Я пришел к следующему, что решает во время компиляции, наследуются ли типы друг от друга. И вроде работает, и мне кажется намного проще понять.

 #include <iostream>
 #include <boost/variant.hpp>
 #include <boost/type_traits.hpp>
 #include <boost/utility.hpp>

 using namespace boost::type_traits;


 struct A { virtual ~A() {} virtual void foo() {} };
 struct B : A { virtual void foo() { std::cout << "B::foo()" << std::endl; } };

  typedef boost::variant<B*,A*,C*> MyVar;


template <typename A,typename B> 
struct types_are_inheritance_related
{ 
 static const bool value=     
 ice_or<
 boost::is_base_of<A, B>::value,
 boost::is_base_of<B, A>::value
 >::value;    
};


 template<class Base>
 class get_visitor
: public boost::static_visitor<Base*> { public:


template<class T>
Base* operator()( T* t, typename boost::enable_if<types_are_inheritance_related<Base,T> >::type* dummy = 0)
{
   Base* b = dynamic_cast<Base*> ( t);
   return b;           
}  

template<class T>
Base* operator()( T* t, typename boost::disable_if<types_are_inheritance_related<Base,T> >::type* dummy = 0)
{       
   return 0;        
}   
};

template<class T>
T* get_var_value(MyVar& var)
{
   get_visitor<T> visitor;
   T* aa= var.apply_visitor(visitor);
   return aa;
}

int main()
{    
 MyVar var = new B;

 A* a = get_var_value<A*>(var); // works!
 a->foo();

 B* b = get_var_value<B*>(var); // works!
 b->foo();
}
person user152508    schedule 23.09.2014