在C ++界面中声明模板函数?

时间:2017-08-03 09:42:42

标签: c++ unit-testing templates interface

为了示范,想象一下我有一些动物类,每一类都来自一个动物'每个班级,每个人都知道'他们是什么类型,每个人都有某种独特的能力:

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;
};

是否有可能解除这个想法,或者为了定义包含它们的接口,模板函数是否永远不会被声明为虚拟函数?

如果这个想法不起作用,那么这个问题何时开始出错?是否在编写模板函数时会自行投射?我是否应停止返回动物对象然后让调用者对演员负责?

3 个答案:

答案 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
   }
};