简体   繁体   English

在C ++接口中声明模板函数?

[英]Declaring template functions in a C++ interface?

For the sake of a demonstration imagine that I have some animal classes, each derived from an 'animal' class, each 'knowing' what type they are, and each having some sort of unique ability: 为了进行演示,假设我有一些动物类,每个动物类都来自“动物”类,每个“知道”它们的类型,并且每个动物都有某种独特的能力:

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

Now, I want to be able to retrieve animals based on their type: 现在,我希望能够根据动物的类型检索它们:

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

And finally, it would be nice to get the actual type of animal back, to make the calling syntax nicer: 最后,最好返回动物的实际类型,以使调用语法更好:

template<typename T>
T& get()
{
    return static_cast<T&>(get(T::type));
}

Now I can write something like this: 现在,我可以这样写:

animal_getter ag;
ag.get<antelope>().run();

rather than the wordier: 而不是单词:

animal_getter ag;
static_cast<antelope&>(ag.get(animal_type::antelope)).run();

I hope there's nothing too unreasonable about that. 我希望这没有什么不合理的。 But now I want to be able to unit test getting an animal, so ideally it will be possible to fake the animal_getter class (imagine the actual implementation accesses a database or something which you don't want in a unit test, hence the fake). 但是现在我希望能够对动物进行单元测试,因此理想情况下可以伪造animal_getter类(想象实际的实现访问数据库或在单元测试中不需要的东西,因此是伪造的) 。 So it would be nice to define an interface for 'animal getter' classes, and then create a fake that implements the interface. 因此,最好为“动物吸气剂”类定义一个接口,然后创建一个实现该接口的伪造品。 And here's the problem, can this interface be written ? 这就是问题, 可以编写此接口吗? This isn't going to compile: 这不会编译:

struct IAnimalGetter
{
    virtual template<typename T> T& get() = 0;
};

Is it possible to rescue this idea or can template functions never be declared a virtual for the purpose of defining an interface that includes them? 是否有可能挽救这个想法,或者为了定义包含模板函数的接口而永远不能将它们声明为虚拟函数?

If the idea is a non-starter, at what point did this start to go wrong? 如果这个主意是一个初学者,那么从什么时候开始就出错了? Was it when the template function was written which does its own casting? 是在编写模板函数时进行自己的转换吗? Should I have stopped at something which returns an animal object and then have the caller be responsible for the cast? 我是否应该停止返回动物物体的东西,然后让调用者来负责演员?

Templates and casts and type tags don't seem to serve any useful purpose in the declared use case (getting an animal and then immediately exploiting its unique characteristics). 模板,演员表和类型标签在声明的用例中似乎没有任何用处(获取动物,然后立即利用其独特特征)。

antelope& get_antelope();
bear& get_bear();
cat& get_cat();

This is all that's needed. 这就是所需要的。

get_antelope().run();
get_cat().meow();

These functions can be standalone or member, virtual if desired. 这些功能可以是独立的也可以是成员的,如果需要的话可以是虚拟的。 You need one function per returned type. 每个返回类型需要一个函数。 This is not very different from, and in some ways superior to, one case in a switch statement per returned type. 这与每种返回类型的switch语句中的一种情况并没有太大区别,并且在某些方面也有优势。

If, for some yet unspecified reason, you do need a function template, you can define one based on plain functions. 如果出于某种尚未确定的原因,您确实需要功能模板,则可以基于普通功能定义一个模板。

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

Virtual template functions are not allowed, but your template function has no logic, so in this case I would use: 不允许使用虚拟模板函数,但是您的模板函数没有逻辑,因此在这种情况下,我将使用:

struct IAnimalGetter
{
    virtual animal& get(animal_type t) = 0;

    template<typename T>
    T& get()
    {
      return static_cast<T&>(get(T::type));
    }
};

And: 和:

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

The use of casting kind of defeats the point of using templates here. 使用强制转换类型失败了在这里使用模板的目的。 Its not really clear to me what the getter class is for. 对我来说,尚不清楚吸气剂类的用途。 Its clear its for holding some static instances of some pre-determined types, but its not clear why its a single class handling all the types. 很明显,它可以容纳某些预定类型的静态实例,但是不清楚为什么它的单个类可以处理所有类型。 I can't see that you gain much as it doesn't have much code in and you have to specify the type in the call anyway. 我看不到您有多少好处,因为其中没有太多代码,而且无论如何您都必须在调用中指定类型。 The switch statement is a potential bug, if you add a new animal type it will still compile but the new type wont be handled in the switch and you get a run time error. switch语句是一个潜在的错误,如果添加新的动物类型,它将仍然可以编译,但是新类型将不会在switch中处理,并且会出现运行时错误。

If you don't need static instances, just construct the correct type. 如果不需要静态实例,则构造正确的类型。

If you do need them then consider whether they should be managed by that type (ie bear has a method to get the bear instance). 如果确实需要它们,则考虑是否应通过该类型对其进行管理(即bear具有获取bear实例的方法)。 Another option (as has already been pointed out) is to expose the static instances of each type your getter holds. 另一个选择(正如已经指出的那样)是公开您的getter持有的每种类型的静态实例。

If you don't want to manage the static instances in the related classes, consider having separate getters: 如果您不想管理相关类中的静态实例,请考虑使用单独的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
   }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM