我需要一个容器/包装器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
更新
编译和运行时故障都可以。
答案 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 */
};