4

我需要一个包含单个任意值的容器/包装器 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

4 回答 4

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上输出。有几种可能的变化。例如,如果您执行 a then如果当前的基础类型不可转换为新类型,dynamic_cast< u->get() >(fitness_->get())则会引发异常。这将允许您更改为派生类型,但不能更改为完全不相关的类型。std::bad_castGenomeGenome

于 2012-08-07T08:45:37.893 回答
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 最近接受了审查。我真的很想尝试用它来实现这个版本的 any 至少看看实现是否允许它。

于 2012-08-07T08:07:51.150 回答
0

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

于 2012-08-07T07:55:30.117 回答
0

我认为您在这里尝试做的问题是模板与所有类型一样,必须在编译时完全指定。鉴于这种限制,像这样的简单类并没有什么好处:

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

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

};
于 2012-08-07T07:57:42.037 回答