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 を実行すると、現在の基になる型が新しい型に変換できない場合、例外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 に答える