我认为没有代码的任何解释都会更加模糊。所以这里的代码是我尽量保持一切尽可能简单的。
#include <vector>
#include <iostream>
class WithParametersBase
{
public:
WithParametersBase();
double getX() const {return 0.0;}
double getY() const {return 1.0;}
//let's say I want to access these members using an unified interface:
double getParameter(int index) const;
// For example index == 0 means getX and index == 1 means getY.
// I could implement it for example like this:
protected:
void addGetter(double (WithParametersBase::* getter)()const)
{
getters_.push_back(getter);
}
std::vector<double (WithParametersBase::*)()const> getters_;
};
WithParametersBase::WithParametersBase()
{
addGetter(&WithParametersBase::getX);
addGetter(&WithParametersBase::getY);
}
double WithParametersBase::getParameter(int index) const
{
return (this->*(getters_[index]))();
}
确实有效。有了测试程序:
int main(int argc, char *argv[])
{
WithParametersBase base;
std::cout << base.getParameter(0)
<< base.getParameter(1) << std::endl;
return 0;
}
打印输出正确无误:
01
但是如果我想扩展这个类:
class WithParametersDerived : public WithParametersBase
{
public:
WithParametersDerived();
double getZ() const {return 2.0;} // A new getter
};
WithParametersDerived::WithParametersDerived()
{
// I want to integrate the new getter into the previous interface
addGetter(&WithParametersDerived::getZ);
}
所以,如果我打电话:
WithParametersDerived derived;
std::cout << derived.getParameter(2) << std::endl;
我想得到一个
2
我无法编译程序。我收到一个错误:
error: no matching function for call to
'WithParametersDerived::addGetter
(double (WithParametersDerived::*)()const)'
哪个是合理的,但我不知道如何实现它。
我希望派生类的创建者能够添加新的getter。我知道,它在某种程度上不能在运行时完成所有这些,但我没有看到模板解决方案或预处理器解决方案。如果您有任何建议,请告诉我。什么!
答案 0 :(得分:6)
我会回避为什么你需要这样的方案,并专注于如何。
您可以使用std::function<double ()>
代替成员函数指针,double foo()
是具有签名std::function<double ()>
的任何可调用实体的通用包装器。要从成员函数和对象实例创建std::bind
,请使用std::function<double ()> callback =
std::bind(&Class::memberFunction, objectInstancePointer);
,如下所示:
std::vector
如果您不使用C ++ 11,则std::function和std::bind也可以作为boost::function和boost::bind在Boost中使用。这些Boost文档大部分(如果不是完全)适用于他们的C ++ 11版本。
您可以使用std::map
按名称索引getter,而不是double
。这可能比维护参数ID号的中心列表更实用。
如果您的参数的类型与std::function
不同,那么您可能需要考虑使用boost::any或boost::variant作为返回类型。
以下是使用std::bind
,std::map
和#include <cassert>
#include <map>
#include <iostream>
#include <functional>
class WithParametersBase
{
public:
WithParametersBase()
{
addGetter("X", std::bind(&WithParametersBase::getX, this));
addGetter("Y", std::bind(&WithParametersBase::getY, this));
}
virtual double getX() const {return 0.0;}
virtual double getY() const {return 1.0;}
// Access parameter by name
double getParameter(const std::string& name) const
{
auto getterIter = getters_.find(name);
assert(getterIter != getters_.end());
return getterIter->second();
}
protected:
typedef std::function<double ()> ParameterGetter;
typedef std::map<std::string, ParameterGetter> GetterMap;
void addGetter(const std::string& name, const ParameterGetter& getter)
{
getters_[name] = getter;
}
GetterMap getters_;
};
class WithParametersDerived : public WithParametersBase
{
public:
WithParametersDerived()
{
addGetter("Z", std::bind(&WithParametersDerived::getZ, this));
// Override base class getX
addGetter("X", std::bind(&WithParametersDerived::getX, this));
}
double getX() const {return 3.0;}
double getZ() const {return 2.0;} // A new getter
};
int main(int argc, char *argv[])
{
WithParametersBase base;
WithParametersDerived derived;
WithParametersBase& polymorphic = derived;
std::cout << base.getParameter("X")
<< base.getParameter("Y")
<< polymorphic.getParameter("X")
<< polymorphic.getParameter("Y")
<< polymorphic.getParameter("Z") << std::endl;
return 0;
}
的完整工作示例:
WithParametersBase
这种方法的缺点是GetterMap
(或后代)的每个实例都包含GetterMaps
。如果您有大量此类对象,则所有std::function
的内存开销可能都是不合需要的。
这是一个更有效的解决方案,可以取消std::bind
和GetterMap
。常规函数指针和静态成员函数用于getter回调。请求参数的对象实例作为参数传递给这些静态成员函数。在派生类型中,在调用实际获取的成员函数之前,首先将实例引用向下转换为派生类型。
现在每个类只有一个getters()
而不是每个对象。请注意WithParametersBase
方法中使用“首次使用时构造”的习惯用法以避免使用static initialization order fiasco。
此解决方案的缺点是,为#include <cassert>
#include <map>
#include <iostream>
class WithParametersBase
{
public:
virtual double getX() const {return 0.0;}
virtual double getY() const {return 1.0;}
// Access parameter by name
double getParameter(const std::string& name) const
{
auto getterIter = getters().find(name);
assert(getterIter != getters().end());
return getterIter->second(*this);
}
protected:
typedef double (*ParameterGetter)(const WithParametersBase& instance);
typedef std::map<std::string, ParameterGetter> GetterMap;
static double xGetter(const WithParametersBase& instance)
{
return instance.getX();
}
static double yGetter(const WithParametersBase& instance)
{
return instance.getY();
}
static GetterMap makeGetterMap()
{
GetterMap map;
map["X"] = &WithParametersBase::xGetter;
map["Y"] = &WithParametersBase::yGetter;
return map;
}
virtual const GetterMap& getters() const
{
// Not thread-safe. Use std::call_once to make thread-safe.
static GetterMap map = makeGetterMap();
return map;
};
};
class WithParametersDerived : public WithParametersBase
{
public:
double getX() const {return 3.0;}
double getZ() const {return 2.0;} // A new getter
protected:
static double zGetter(const WithParametersBase& instance)
{
// It's reasonably safe to assume that 'instance' is of type
// WithParametersDerived, since WithParametersDerived was the one
// that associated "Z" with this callback function.
const WithParametersDerived& derived =
dynamic_cast<const WithParametersDerived&>(instance);
return derived.getZ();
}
static GetterMap makeGetterMap()
{
// We "inherit" the getter map from the base class before extending it.
GetterMap map = WithParametersBase::makeGetterMap();
map["Z"] = &WithParametersDerived::zGetter;
return map;
}
virtual const GetterMap& getters() const
{
// Not thread-safe. Use std::call_once to make thread-safe.
static GetterMap map = makeGetterMap();
return map;
};
};
int main(int argc, char *argv[])
{
WithParametersBase base;
WithParametersDerived derived;
WithParametersBase& polymorphic = derived;
std::cout << base.getParameter("X")
<< base.getParameter("Y")
<< polymorphic.getParameter("X")
<< polymorphic.getParameter("Y")
<< polymorphic.getParameter("Z") << std::endl;
return 0;
}
派生的每个类编写了更多的样板代码。有可能使用模板减少样板代码的数量(绝对可以使用宏)。
{{1}}