C ++中单一类型和单一元素的通用容器

时间:2012-08-07 07:44:35

标签: c++ templates generics containers

我需要一个容器/包装器C ++类,它包含一个任意值。设置此值后,只接受相同类型的值。

这是我一直在尝试的代码。

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;
}           
};

虽然Genome可以保存任意值,但是一旦设置了第一个,它就不会限制类型,即以下代码有效:

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

更新

编译和运行时故障都可以。

4 个答案:

答案 0 :(得分:2)

除了使用像Boost.Any这样的库之外,对源代码的一个小修改已经有效了。只需添加一个模板化的构造函数来初始化Genome,并使用新旧类型之间的(实现定义的)typeid()运算符进行相等性测试。

#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;
}

Ideone上的输出。有几种可能的变化。例如。如果您执行dynamic_cast< u->get() >(fitness_->get()),那么如果当前基础类型std::bad_cast无法转换为新类型,则会抛出Genome异常。这样您就可以将Genome更改为派生类型,但不能更改为完全不相关的类型。

答案 1 :(得分:1)

此代码基于boost::any。它使用类型擦除来存储 任意值并禁止分配。我条纹了 铸造机械,使眼睛更容易。

也许你可以在包装boost::any方面取得一些成功,而不是带来完整的重新实现,但我不确定这一点,你需要对这些演员进行一些关注。

它还禁止复制和移动,因为我不能打扰交易 有了它,但你的全部实现应该有它。

你也想给它一个更有意义的名字。

#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;
}

无关的说明:Boost.TypeErasure最近经过审核。我真的很想尝试用它来实现这个版本,至少看看实现是否允许它。

答案 2 :(得分:0)

我不知道这种容器类型,但我有一个建议:为什么不在setFitness()中添加一个检查值的类型是否与当前值的类型相同?这样你就可以保证每次都是同一种类型。

答案 3 :(得分:0)

我认为你在这里尝试做的问题是,所有类型的模板都必须在编译时完全指定。鉴于这种限制,对于这样一个简单的类没有太多的好处:

template<typename T>
class Fitness {
    T value;        
    Fitness(T value) : value(value){}

    /* Copy construct, assign, & Compare in the usual way */

};