为了示范,想象一下我有一些动物类,每一类都来自一个动物'每个班级,每个人都知道'他们是什么类型,每个人都有某种独特的能力:
enum class animal_type { antelope, bear, cat };
class animal
{
};
class antelope : public animal
{
public:
static const animal_type type = animal_type::antelope;
void run() { std::cout << "antelope runs\n"; };
};
class bear : public animal
{
public:
static const animal_type type = animal_type::bear;
void roar() { std::cout << "bear roars\n"; };
};
class cat : public animal
{
public:
static const animal_type type = animal_type::cat;
void meow() { std::cout << "cat meows\n"; };
};
现在,我希望能够根据类型检索动物:
class animal_getter
{
public:
animal& get(animal_type t)
{
static antelope s_antelope;
static bear s_bear;
static cat s_cat;
switch (t)
{
case animal_type::antelope:
return s_antelope;
case animal_type::bear:
return s_bear;
case animal_type::cat:
return s_cat;
}
}
};
最后,让实际的动物类型回来会很好,使调用语法更好:
template<typename T>
T& get()
{
return static_cast<T&>(get(T::type));
}
现在我可以这样写:
animal_getter ag;
ag.get<antelope>().run();
而不是wordier:
animal_getter ag;
static_cast<antelope&>(ag.get(animal_type::antelope)).run();
我希望这没有什么不合理的。但是现在我希望能够对动物进行单元测试,所以理想情况下可以伪造animal_getter类(想象实际实现访问数据库或者你在单元测试中不想要的东西,因此假)。因此,为“动物吸气者”定义界面会很不错。类,然后创建一个实现接口的假。这就是问题,可以编写这个界面吗?这不会编译:
struct IAnimalGetter
{
virtual template<typename T> T& get() = 0;
};
是否有可能解除这个想法,或者为了定义包含它们的接口,模板函数是否永远不会被声明为虚拟函数?
如果这个想法不起作用,那么这个问题何时开始出错?是否在编写模板函数时会自行投射?我是否应停止返回动物对象然后让调用者对演员负责?
答案 0 :(得分:1)
模板和强制转换以及类型标记似乎在声明的用例中没有任何用处(获取动物然后立即利用其独特的特征)。
antelope& get_antelope();
bear& get_bear();
cat& get_cat();
这就是所需要的。
get_antelope().run();
get_cat().meow();
如果需要,这些功能可以是独立的,也可以是成员的,虚拟的。每个返回类型需要一个函数。这与每个返回类型的switch语句中的一个案例没有太大区别,并且在某些方面优于它。
如果由于某些未指明的原因,您确实需要一个功能模板,您可以根据普通函数定义一个。
template <class A>
A& get_animal();
template<> antelope& get_animal<antelope>() { return get_antelope(); }
template<> bear& get_animal<bear>() { return get_bear(); }
template<> cat& get_animal<cat>() { return get_cat(); }
答案 1 :(得分:0)
不允许使用虚拟模板函数,但模板函数没有逻辑,所以在这种情况下我会使用:
struct IAnimalGetter
{
virtual animal& get(animal_type t) = 0;
template<typename T>
T& get()
{
return static_cast<T&>(get(T::type));
}
};
和
class animal_getter : public IAnimalGetter
{
public:
animal& get(animal_type t)
{
// implementation
}
};
class mock_animal_getter : public IAnimalGetter
{
public:
animal& get(animal_type t)
{
// mock implementation
}
};
答案 2 :(得分:0)
使用铸造类型在这里失败了使用模板的重点。 我不清楚getter类的用途。它清楚它用于保存一些预定类型的静态实例,但不清楚为什么它的单个类处理所有类型。我无法看到你获得了很多,因为它没有很多代码,你必须在呼叫中指定类型。 switch语句是一个潜在的错误,如果你添加一个新的动物类型,它仍然会编译,但新的类型不会在交换机中处理,你会得到一个运行时错误。
如果您不需要静态实例,只需构造正确的类型即可。
如果你确实需要它们,那么考虑它们是否应该由该类型管理(即熊有一个获取熊实例的方法)。另一个选项(已经指出)是公开getter所持有的每种类型的静态实例。
如果您不想管理相关类中的静态实例,请考虑使用单独的getter:
template<typename T>
struct IGetter
{
virtual T Get() =0;
};
template<typename T>
struct RealGetter : public IGetter<T>
{
static T item;
virtual T Get()
{
return item;
}
};
template<typename T>
struct MockGetter : public IGetter<T>
{
virtual T Get()
{
// some mock implementation
}
};