【发布时间】:2015-01-03 18:10:00
【问题描述】:
考虑以下代码:
#include <iostream>
struct ActionOption {
virtual void foo(int) const = 0;
};
template <int> struct ActionType;
template <> struct ActionType<0> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<0>::foo(int) called.\n";}
};
template <> struct ActionType<1> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<1>::foo(int) called.\n";}
};
template <> struct ActionType<2> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";}
};
template <> struct ActionType<3> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";}
};
template <> struct ActionType<4> : ActionOption {
virtual void foo(int) const override {std::cout << "ActionType<4>::foo(int) called.\n";}
};
template <int...> struct PossibleActions;
template <> struct PossibleActions<> { void operator()(int) const {} };
template <int First, int... Rest>
struct PossibleActions<First, Rest...> : ActionType<First>, PossibleActions<Rest...> {
void operator()(int a) const {
ActionType<First>::foo(a);
PossibleActions<Rest...>::operator()(a);
}
};
// Anything that can call ActionType<2>::foo(int) can also call ActionType<3>::foo(int).
struct Object : PossibleActions<1, 2,3, 4> {
void foo(int a) {PossibleActions<1,2,3,4>()(a);}
};
struct Blob : PossibleActions<0, 2,3, 4> {
void foo(int a) {PossibleActions<0,2,3,4>()(a);}
};
int main() {
Object object;
object.foo(12); // ActionType<1>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
Blob blob;
blob.foo(12); // ActionType<0>::foo(int) called ActionType<2>::foo(int) called ActionType<3>::foo(int) called ActionType<4>::foo(int) called
std::cout << std::endl;
}
它运行除了这里是问题:任何可以调用ActionType<2>::foo(int) 也可以调用ActionType<3>::foo(int)。因此,每次我定义一个新类时,如果我使用 2 或 3,我必须在 PossibleActions<I...> 中同时使用两者。这对于维护当然是有问题的(比如我决定将来使用 2 也必须使用 3、7 和 20)。以下解决方案:
using TwoAndThree = PossibleActions<2,3>;
struct Object : PossibleActions<1,4>, TwoAndThree {
void foo(int a) {PossibleActions<1,4>()(a); TwoAndThree()(a);}
};
struct Blob : PossibleActions<0,4>, TwoAndThree {
void foo(int a) {PossibleActions<0,4>()(a); TwoAndThree()(a);}
};
不可接受,因为我需要以 数字顺序 调用 ActionType<N>::foo(int)。拆分PossibleActions<1,4>()(a); 也是一个非常的糟糕解决方案,因为它遇到了相同的维护问题(我认为使维护变得更糟)。
template <> struct ActionType<2> : ActionOption { virtual void foo(int) const override {std::cout << "ActionType<2>::foo(int) called.\n";} };
template <> struct ActionType<3> : ActionType<2> { virtual void foo(int) const override {std::cout << "ActionType<3>::foo(int) called.\n";} };
由于歧义而无法编译(并且使用虚拟继承没有帮助),我想不出其他任何东西。这个问题有解决办法吗?
也许用template <typename... Args> struct PossibleActions; 重新定义可能的操作?但是随后递归丢失了。
是吗?
相关问题: 有没有办法使用 Args 进行递归...其中某些类型是 int 但有些不是(以及那些不使用定义这些类型的 int 的递归)类型)?例如
PossibleActions<1, TwoAndThree, 4, EightAndTen, 20>()(a);
根据需要遍历 1,2,3,4,8,10,20,因为 TwoAndThree = PossibleActions<2,3> 和 EightAndTen = PossibleActions<8,10>???如果可能,那将解决问题。
【问题讨论】:
-
我不确定,但 virtual inheritance 可以帮助解决歧义吗?
-
这是我尝试的第一件事,但是递归干扰了它,无法编译。但这并不意味着虚拟继承不起作用。
-
也许你也应该在你的问题中指出这一点。不过,这是一种有趣的函数式编程方法。
-
@prestokeys 可能不是最有效的模板排序器,但how about this ?
-
@Piotr S. 很好的开始!所以想法是简单地遵循上述有缺陷的解决方案,但通过排序来纠正它。好的,那我们还是要泛化
Sort<T...>,这样using OneAndFour = PossibleActions<1,4>; struct Widget : Sort<0, OneAndFour, TwoAndThree, 5,8,13> {};才能编译。我将尝试跟进您的第一个解决方案。
标签: c++ templates c++11 variadic