问题描述
我想创建一个“映射器”系统,即采用输入(模板)值并返回输出(模板)值的对象。映射器可以具有其他功能/特征。
示例代码:
// a trait
template <typename I,typename O>
class Writable
{
public:
virtual ~Writable() = default;
virtual O & operator()(I i) = 0;
};
// another trait
template <typename I,typename O>
class Bounded
{
public:
virtual ~Bounded() = default;
virtual O min() const = 0;
virtual O max() const = 0;
};
// base class
template <typename I,typename O,template<typename,typename> class ... Traits>
class IMap
: public Traits<I,O>...
{
public:
virtual ~IMap() = default;
virtual O operator()(I i) const = 0;
};
// a test mapper
class Test1
: public IMap<int,double,Writable,Bounded>
{
public:
double operator()(int i) const override { return _d; }
double & operator()(int i) override { return _d; }
double min() const override { return 0; }
double max() const override { return 1; }
private:
double _d;
};
// another test mapper
class Test2
: public IMap<int,Bounded,Writable>
{
public:
double operator()(int i) const override { return _d; }
double & operator()(int i) override { return _d; }
double min() const override { return 0; }
double max() const override { return 1; }
private:
double _d;
};
// a class to use mappers
template <typename I,typename O>
class MapUse
{
public:
// I would like this function to accept an IMap containing "at least" the Bounded trait
// Ideally this function signature should be like
void method_requiring_writable(IMap<I,O,Bounded> & map)
{
map(123)
map(123) = 0;
}
// I would like this function to accept an IMap containing "at least" the Bounded and Writable traits
// (no matter the actual order of those traits in variadic template parameter )
// Ideally this function signature should be like
void method_requiring_bounded_and_writable(IMap<I,Writable> & map)
{
map(123);
map(123) = 0;
map.min();
}
};
void test()
{
Test1 t1;
Test2 t2;
Test2 * p = dynamic_cast<Test2*>(&t1); // << always return nullptr,of course !
MapUse<int,double> mu;
mu.method_requiring_writable(t1); // << does not compile,not the right type
mu.method_requiring_bounded_and_writable(t1); // << does not compile,not the right type
return;
}
第一行错误:mu.method_requiring_writable(t1):
error C2664: 'void MapUse<int,double>::method_requiring_writable(IMap<I,Bounded> &)': cannot convert argument 1 from 'Test1' to 'IMap<I,Bounded> &'
> with
> [
> I=int,> O=double
> ]
第一行错误:mu.method_requiring_bounded_and_writable(t1):
error C2664: 'void MapUse<int,double>::method_requiring_bounded_and_writable(IMap<int,Writable> &)': cannot convert argument 1 from 'Test1' to 'IMap<int,Writable> &'
我完全理解为什么这行不通,但是我想让它起作用!
第一个问题:可变参数模板参数中的特征顺序不计算在内。可以通过强制开发人员遵守特征顺序来轻松解决此问题,但这并不理想
第二个问题:能够创建需要“至少”具有一组特定特征的映射的方法。对此一无所知。
第一种方法不是可变的,而是-具有非常多的虚拟遗产的-非常复杂的层次结构->地狱般的丑陋,添加新特性真是痛苦!
使用上面的代码,我曾考虑过在IMap中添加某种类型的转换运算符,但没有这样做。
任何C ++ / variadic /魔术专家可以帮助您解决此问题吗?
解决方法
您可以使用类似这样的内容:
#include <type_traits>
template <template <class,class> class... Traits,std::enable_if_t<
std::is_base_of<Bounded<I,O>,IMap<I,O,Traits...>>::value
and std::is_base_of<Writable<I,Traits...>>::value,int> = 0>
void method_requiring_bounded_and_writable(IMap<I,Traits...> & map) {
// Do stuff
}
,
您为什么不首先检查混合类型?您在这里混合了通用和非通用编程。只需使用已实现的特征(例如min()
和max()
),而无需注意它们是如何实现的。简便易行的 clean 解决方案是继续使用通用编程:
//template <typename I,typename O>
class MapUse {
public:
template <typename T>
void method_requiring_writable(T& map) {
map(123);
map(123) = 0;
}
template <typename T>
void method_requiring_bounded_and_writable(T& map) {
map(123);
map(123) = 0;
map.min();
}
};
其他解决方案需要大量的元编程,只会使您的代码更加复杂且难以阅读。我了解这可能无法解决您的特定问题。这是一个建议,可以重新考虑您的设计,并完全跳入或完全放弃通用火车。