Generic container for one type and one element in C ++

I need a C ++ class for a container / shell that contains a single, arbitrary value. When this value is set, only values ​​of the same type should be accepted.

This is the code I experimented with.

struct Genome { struct FitnessConcept {}; template<typename T> struct Fitness : public FitnessConcept{ T value; Fitness(T value) : value(value){} }; std::shared_ptr<FitnessConcept> fitness; template<typename T> void setFitness(T value) { fitness.reset(new Fitness<T>(value)); } template<typename T> T getFitness() { return static_cast<Fitness<T>*>(fitness.get())->value; } }; 

Although Genome may contain arbitrary values, it does not limit the type after the first, that is, the following code, is installed:

 Genome g; g.setFitness(0.2); g.setFitness("foo"); //this should fail 

UPDATE

Compilation and execution fail in order.

+4
source share
4 answers

Besides using libraries like Boost.Any , a small modification to your source already works. Just add a template constructor to initialize Genome and get an equality test using the (implementation-defined) operator typeid() between the new and old types.

 #include<stdexcept> #include<memory> #include<typeinfo> class Genome { private: class FitnessConcept { public: virtual ~FitnessConcept() {} }; template<typename T> class Fitness : public FitnessConcept { public: explicit Fitness(T v) : value_(v) {} T value() { return value_; } private: T value_; }; public: template<typename T> explicit Genome(T v) : fitness_(new Fitness<T>(v)) {} template<typename T> void setFitness(T v) { auto u = std::make_shared< Fitness<T> >(v); if (typeid(fitness_).name() == typeid(u).name()) fitness_ = u; else throw std::invalid_argument("cannot change initialized genome type\n"); } template<typename T> T getFitness() { return static_cast<Fitness<T>*>(fitness_.get())->value(); } private: std::shared_ptr<FitnessConcept> fitness_; }; int main() { Genome g(1.0); // OK g.setFitness(2.0); // OK g.setFitness("3.0"); // throws exception return 0; } 

Conclusion on Ideone . Several options are possible. For instance. if you execute dynamic_cast< u->get() >(fitness_->get()) then this will throw a std::bad_cast exception if the current Genome type is not converted to a new type. This will allow you to change the Genome to a derived type, but not to completely unrelated types.

+2
source

This code is based on boost :: any . It uses type erasure to store an arbitrary value and forbids it from being assigned. I stopped casting to lighten my eyes.

You may have some success in boost::any wrapper instead of bringing a complete reevaluation, but I'm not so sure and you will need to take care of the ghosts a bit.

It also prohibits copying and moving, because I cannot be bothered by the deal with this, but your full implementation should have it.

You also want to give him a name that makes more sense.

 #include <typeinfo> #include <iostream> class reassign_any { public: reassign_any() : content_(nullptr) {} template <typename T> reassign_any(const T& x) : content_(new holder<T>(x)) {} ~reassign_any() { // no need to check for null delete content_; } reassign_any(const reassign_any&) = delete; reassign_any& operator=(const reassign_any&) = delete; reassign_any(reassign_any&&) = delete; reassign_any& operator=(reassign_any&&) = delete; bool empty() const { return !content_; } template <typename T> bool set(const T& t) { if(content_) { // check for type equality of held value and value about to be // set if(content_->type() == typeid(T)) { delete content_; content_ = new holder<T>(t); return true; } else { return false; } } else { // just create content_ = new holder<T>(t); return true; } } template <typename T> T* get() { return content_->type() == typeid(T) ? &static_cast<holder<T>*>(content_)->held : 0; } private: class placeholder { public: // structors virtual ~placeholder() { } virtual const std::type_info & type() const = 0; }; template<typename ValueType> class holder : public placeholder { public: // structors holder(const ValueType & value) : held(value) { } virtual const std::type_info & type() const { return typeid(ValueType); } public: // representation ValueType held; private: // intentionally left unimplemented holder & operator=(const holder &); }; private: placeholder* content_; }; int main() { reassign_any x; x.set(3); if(x.get<int>()) std::cout << "int set" << std::endl; if(x.set(3.f)) { std::cout << "this shouldn't be printed" << std::endl; } else { std::cout << "this should be printed" << std::endl; } if(x.set(23)) { std::cout << "this should be printed" << std::endl; } else { std::cout << "this shouldn't be printed" << std::endl; } return 0; } 

Unrelated Note: Boost.TypeErasure recently went through a review. I would really like to try and implement this version of any of them, at least in order to see if the implementation allows.

+1
source

I don’t know this type of container, but I have a suggestion: why not add a check to setFitness() , is the value type the same as the type of the current value? This way you can guarantee that the type will be the same every time.

0
source

I think the problem with what you are trying to do here is that the templates, like all types, must be fully specified at compile time. Given this limitation, there is not much you can get per simple class:

 template<typename T> class Fitness { T value; Fitness(T value) : value(value){} /* Copy construct, assign, & Compare in the usual way */ }; 
0
source

All Articles