我有一个类似于以下的类:
class SomeClass
{
public:
template<typename... Args>
void doSomething(Args && ... args);
//... other methods etc.
};
但是,我真正想要的是拥有两种不同的SomeClass
。理想情况下,我可以从一个公共接口派生出SomeOtherClass
,但我需要有doSomething
的不同实现,并且模板化方法不能是虚拟的。我可以创建一个模板化的类,但是每个采用其中一个(并且有很多)的方法本身都必须是模板等。
我能想到的最好的方法是在基类中实现两种类型的doSomething
,并让该方法调用一个虚方法来确定在运行时使用哪种方法。
有更好的解决方案吗?
我有很多类似的方法:
void foo(SomeClass * obj);
foo
调用obj->doSomething
这一切都运行良好,但我已经意识到我需要一种不同类型的SomeClass
,但希望它能够使用这些相同的方法,例如:
class SomeClass
{
public:
// This won't work
template<typename... Args>
virtual void doSomething(Args && ... args) = 0;
// ... other common methods
};
class TheFirstType
{
public:
template<typename... Args>
void doSomething(Args && ... args);
// ... other specific methods
};
class TheSecondType
{
public:
template<typename... Args>
void doSomething(Args && ... args);
// ... other specific methods
};
如果它是合法的,上述将是理想的,但虚拟方法不能被模板化。到目前为止,我只是通过在基类中定义doSomething
来解决这个限制,但TheFirstType
和TheSecondType
的实现由if语句分隔,它检查什么类型实例实际上是:
template<typename... Args>
void SomeClass::doSomething(Args && ... args)
{
if (this->type() == FIRST_TYPE) {
// ... the implementation that should rightfully be part of TheFirstType
} else if (this->type() == SECOND_TYPE) {
// ... the implementation that should be part of TheSecondType
}
}
然而,这似乎很混乱,所以我想知道是否有更好的方法。
答案 0 :(得分:6)
我认为@stijn的回答是正确的;你有CRTP的理想情况。你可以选择改变你的逻辑。
template<class T>
class SomeClass
{
public:
template<typename... Args>
void doSomething(Args && ... args)
{
static_cast<T*>(this)->doSomething(...);
}
//other method can be virtual
virtual void foo ()
{
doSomething(...);
// ... other code;
}
};
现在只需将这些class
继承到其他子类:
class TheFirstType : SomeClass<TheFirstType>
{
public:
template<typename... Args>
void doSomething(Args && ... args) { ... }
virtual void foo ()
{
}
}; // do same for TheSecondType.
你已经完成了。
答案 1 :(得分:2)
我的猜测是你在CRTP之后(虽然我无法确定,正如iammilind在评论中指出的那样):
template< class Parent >
class SomeClassBase : public Parent
{
public:
//common methods go here
};
class SomeClass : public SomeClassBase< SomeClass >
{
public:
template<typename... Args>
void doSomething(Args && ... args);
};
class SomeOtherClass : public SomeClassBase< SomeOtherClass >
{
public:
template<typename... Args>
void doSomething(Args && ... args);
};
答案 2 :(得分:0)
在这种情况下可能有用的是在包装类SomeClass
中使用宏函数。宏的好处是文本替换。通常我们不能将一个函数及其所有参数作为C ++中的对象传递(至少在C ++ 11之前,过去,我不确定)。但是,使用宏,我们可以传递任何类型的文本。这里的额外好处是它将为每个具有此模板虚拟冲突的函数删除重复的if / else代码。
我们可以按如下方式设置宏:
#define CLASS_SELECT(func) \
do { \
if (this->type() == FIRST_TYPE) { \
theFirstType.func; \
} else if (this->type() == SECOND_TYPE) { \
theSecondType.func; \
} \
} while (0)
如果你不熟悉封装时的做法,look here。该类将使用宏,如:
class SomeClass
{
TheFirstType theFirstType;
TheSecondType theSecondType;
public:
template<typename... Args>
void doSomething(Args && ... args)
{
CLASS_SELECT(doSomething (&args...) );
}
};
在上面的示例中,我实例化了SomeClass
中的专用对象。我不确定这是否是你想要的设计,如果不是,我会考虑在宏中使用静态getter函数等。
无论如何,我承认我的解决方案可能并不理想,但它是我迄今为止所做的最好的(我在代码中遇到了与你相同的问题)。我承认在其他答案中引用的CRTP非常酷,虽然它不符合我的需要(必须在参数中指定模板也不会为我飞行,特别是在运行时)。