The usual template for this is the virtual cloning method through your hierarchy.
If this is not possible, is there any non-ugly solution to this problem that anyone knows about?
You can also use an instance of the clone function template based on copy constructors. Here is the solution I am using on the web server that I am writing (pet project):
#pragma once #include <memory> #include <cassert> #include <functional> #include <stdexcept> #include <vector> namespace stdex { inline namespace details { /// @brief Deep copy construct from (Specialized&)*src /// /// @retval nullptr if src is nullptr /// @retval Specialized clone of *src /// /// @note Undefined behavior src does not point to a Specialized* template<typename Base, typename Specialized> Base* polymorphic_clone (const Base* src) { static_assert(std::is_base_of<Base, Specialized>::value, "Specialized is not a specialization of Base"); if (src == nullptr) return nullptr; return new Specialized{ static_cast<const Specialized&>(*src) }; } } /// @brief polymorphic reference interface over a base class /// /// Respects polymorphic behavior of class ref. /// Instances have deep copy semantics (clone) and /// "[const] Base&" interface /// /// @note Not regular: no trivial way to implement non-intrusive equality /// /// @note safe to use with standard containers template<typename Base> class polymorphic final { public: /// Functor capable to convert a Base* to it specialized type /// and clone it (intrusive implementation can be used) typedef std::function<Base* (const Base*)> clone_functor; /// @brief construct (takes ownership of ptr) template<typename Specialized, typename CloneSpecialized> polymorphic(Specialized* ptr, CloneSpecialized functor) noexcept : instance_{ptr}, clone_{std::move(functor)} { static_assert(std::is_base_of<Base, Specialized>::value, "Specialized is not a specialization of Base"); static_assert( std::is_constructible<clone_functor, CloneSpecialized>::value, "CloneSpecialized is not valid for a clone functor"); } // not implemented: UB cloning in case client provides specialized ptr // polymorphic(Base* ptr); polymorphic() : polymorphic{ nullptr, clone_functor{} } { } polymorphic(polymorphic&&) = default; polymorphic(const polymorphic& other) : polymorphic{std::move(other.clone())} { } polymorphic& operator=(polymorphic other) { std::swap(instance_, other.instance_); std::swap(clone_, other.clone_); return *this; } ~polymorphic() = default; /// @brief Cast to contained type /// @pre instance not moved /// @pre *this initialized with valid instance operator Base&() const { assert(instance_.get()); return *instance_.get(); } /// @brief Cast to contained type /// @pre instance not moved /// @pre *this initialized with valid instance operator const Base&() const { assert(instance_.get()); return *instance_.get(); } private: polymorphic clone() const { return polymorphic{ clone_(instance_.get()), clone_functor{clone_} }; } std::unique_ptr<Base> instance_; clone_functor clone_; }; template<typename Base, typename Specialized, typename CF> polymorphic<Base> to_polymorphic(Specialized&& temp, CF functor) { static_assert(std::is_base_of<Base, Specialized>::value, "Specialized is not a specialization of Base"); typedef typename polymorphic<Base>::clone_functor clone_functor; auto ptr_instance = std::unique_ptr<Base>{ new Specialized{std::move(temp)} }; auto clone_instance = clone_functor{std::move(functor)}; return polymorphic<Base>{ptr_instance.release(), clone_instance}; } template<typename Base, typename Specialized> polymorphic<Base> to_polymorphic(Specialized&& temp) { static_assert(std::is_base_of<Base, Specialized>::value, "Specialized is not a specialization of Base"); return to_polymorphic<Base,Specialized>( std::move(temp), details::polymorphic_clone<Base,Specialized> ); } template<typename Base, typename Specialized, typename ...Args> polymorphic<Base> to_polymorphic(Args ...args) { static_assert(std::is_constructible<Specialized, Args...>::value, "Cannot instantiate Specialized from arguments"); return to_polymorphic<Base,Specialized>( std::move(Specialized{std::forward<Args...>(args...)})); } template<typename Base> using polymorphic_vector = std::vector<polymorphic<Base>>; template<typename Base, typename ...Args> polymorphic_vector<Base> to_polymorphic_vector(Args&& ...args) { return polymorphic_vector<Base>{to_polymorphic<Base>(args)...}; } } // stdex
Usage example:
stdex::polymorphic_vector<view> views = // explicit type for clarity stdex::to_polymorphic_vector( echo_view{"/echo"}, // class echo_view : public view directory_view{"/static_files"} // class directory_view : public view ); for(auto& v: views) if(v.matches(reuqest.url())) // bool view::matches(...); auto response = view.handle(request); // virtual view::handle(...) = 0;
Limitations:
If you are using multiple inheritance, DO NOT use stdex :: details :: polymorphic_clone. Instead, write an implementation based on dynamic_cast and use to_polymorphic(Specialized&& temp, CF functor) .