Bad use of templates?

I understand that templates seem to be accused of binary bloating, I also understand that a template is just a template. I really do not understand the nuts and bolts, as there.

For some time I see the code as shown below, where it returns a pointer to the base class.

class GameObject { public: Component* getComponent(std::string key); }; static_cast<Comp>(obj.getComponent("Comp")); 

Instead of making a method a template method.

 class GameObject { public: template<typename T> T* getComponent(std::string key); }; obj.getComponent<Comp>("Comp"); 

Is it stylistic or is there a performance loss associated with templates?

+6
source share
6 answers

The fact that the method accepts a "key", which seems to be really a type (the type of the returned component), tells me that the type is still unknown before execution. If this is the case, then a compile-time mechanism such as templates will not execute.

The only option is to return a pointer to the base class. And usually, when this template is used, only virtual methods are called to the base class, so the actual type of the derived class does not matter (so static_cast or dynamic_cast is not required).

Edit:

As PhilCK noted in the comments, this type is actually known at compile time. If so, then a dynamic type search was never required, and simple factory methods could be used:

 class GameObject { A getComponentA(); B getComponentB(); C getComponentC(); // etc. } // which is more or less identical to: class ComponentFactory { public: virtual Component* create() = 0; }; class GameObject { std::map<std::string,ComponentFactory> m; public: GameObject() { // presumably a private map has to be populated with string -> factory methods here } template<class T> T* getComponent(const std::string& s) { // either the static_cast is needed here, or an alternate (messier) // implementation of getComponent is needed. // it still possible for the wrong type to be passed (by mistake) // and the compiler won't catch it (with static_cast). Since this could lead // to heap corruption the only safe way with this type of // implementation is dynamic_cast. return static_cast<T>(m[s].create()); } }; // this doesn't even compile: // return types: class GameObject { template <class T> T* getComponent(const std::string& s) { if (s == "A") return new A(); else if (s == "B") return new B(); // etc.. else throw runtime_error("bad type"); } } 

So, there are two options, as I see it.

1) use simple factory methods in which templates are not needed at all. 2) use the implementation of the map β†’ factory along with dynamic_cast (which seems to be aimed at using the creation of a dynamic type) and is actually unnecessarily complicated if dynamic types are not needed

+3
source

In zero order there should be no difference in performance.

The template method will create a member function for each T This does not make this code slower as such, but it can make the call more costly due to problems with the locality of the code, since the cast probably occurs further from the call site. Only profiling can tell you.

There are probably other bottlenecks to worry about, for example, passing arguments by value rather than by const reference.

 Component* getComponent(std::string key); Component* getComponent(const std::string& key); 

In addition, this function may be const .

+1
source

If I understand your example correctly, the question is not whether templates inflate binary files, but whether templates or polymorphism should be used.

In short, templates allow flexible types at compile time, and polymorphism allows flexible types at runtime using inheritance. Of course, there is much more. Which one depends on your real needs. In your case, I would say that it depends on the relationship between the different types of components. If they have common functionality, they should have a common base class, here Component . If they have nothing in common, you should use templates.

WRT performance, if you are not in real time, usually this will not be the main reason for choosing between them. Templates can generate even less binary and then regular classes, because only what you use is created, and the overhead of virtual functions (used in inheritance) is subtle. So this is usually a matter of style (or best practice).

0
source

The compiler converts the template code into an assembly. If this happens for many types, it will cause the assembly code blocks to repeat again. This can be avoided without using templates. Or often the use of templates is a wrapper function for functions without templates or a small number of template functions. Loss of performance will be associated with large files. On the other hand, a general function can be more complicated due to the use of pointers / references instead of objects. And the compiler has less ability to embed them.

Another reason for bloating is that a template function is created in every dll they use.

0
source

Bloat can be a problem with patterns, and bloating alone can cause performance problems. However, a good compiler can optimize some bloating problems, and there are programming methods that (when necessary) can avoid the rest.

This should not be a problem here, however - your template function is likely to be trivial and therefore will be inline. This can, in principle, cause some bloating in itself, but since the function is trivial, the amount of β€œbloat” is also trivial.

A serious problem with your sample example is that it tries to overload the method only by the return value. There are languages ​​(such as Ada) that can allow overloads using the return type, but C ++ is not one of them.

Therefore, you need another way to determine which version of the method you intend to invoke. One of the easiest ways (if there are only a few types to worry about) is to declare and define these methods separately, each with a different name.

0
source

It looks like your use is related to one way or another. In this case, I will throw in the template method that is under your control.

The main difference between the two approaches is that the first fragment does not check if it is actually executed for translation (and if you do not use dynamic_cast , this cannot be verified).

In the second fragment, it is possible for the class to select or return a NULL pointer if the type to be distinguished is not an object type by this identifier.

I would suggest that the bloat template, if there is one, will be minimal, since you could delegate the real work to the non-template helper anyway, and only the template that will do extra should execute static_cast . When it is built-in, it will be the same as the version without the template (except that you can make the function smarter by checking the type of result).

0
source

Source: https://habr.com/ru/post/895145/


All Articles