Using a Template to Provide a Private Typedef

I have a class that contains a private typedef and several members of a Function:

class Foo { private: typedef std::blahblah FooPart; FooPart m_fooPart; ... public: int someFn1(); int someFn2(); }; 

Several member functions should use m_fooPart in a similar way, so I want to put this in a function. I put helper functions in anonymous namespaces whenever possible, but in this case they need to know what FooPart is. So, I did this:

 namespace { template <typename T> int helperFn(const T& foopart, int index) { ... return foopart.fn(index); } } int Foo::someFn1() { ... return helperFn(m_fooPart, ix); } 

Forcing the compiler to create a FooPart type, am I still in a land of well-defined behavior? Is there a more elegant way of doing this that doesn't increase the size of Foo or makes it publicly available, which is now private?

+4
source share
3 answers

Yes, this approach provides clear, standard behavior.

However, adding member functions to the class does not increase the size of the class (assuming that you mean the result of the sizeof operator), so I'm not sure what kind of flaw you take when creating a helper function, the private member Foo .

+4
source

The simple answer is: make typedef post open.

This will leak a minor implementation detail (actual internal type), but since it has been changed, you can redefine it at any time, and that should be fine.

A little less simple: make friends with a helper function by providing access to your inner type.

The problem with this second approach is that you not only provide access to typedef, but also to all the private parts of your class, and this may not be the best idea. In any case, since this is an internal auxiliary function, it is under your control, and everything should be fine. (Now that I think about it, you might want to declare a function in a named namespace so that the friend declaration succeeds)

Even less simple: create a separate typedef file inside the implementation file and make sure they are in sync.

You can guarantee that the types will be the same with a little metaprogramming, with the same_type<T,U> pattern that will provide true if the two types are the same and false otherwise. Static statement throws error if typedef changes in only one place

To return to the simple: enter typedef or use the type directly without static assert.

You call the function (it should not be a template, as in your code) and pass the link. If the typedef changes in the class, the call will fail and the compiler will tell you.

I would use the latter option, although it may look a little rude and less delicate than others, the fact is that this is only an implementation detail that is not used by others, you have full control over the code and it’s good, simpler.

EDIT after comment.

I started writing this as a comment, but it got too long, so I am adding it to the answer.

There is nothing wrong with this solution, except that you arbitrarily perform the generic function, and some error messages in the future may not be as simple as they may be with a non-piggy signature. Note that template will not expose a typedef (as the name of the question suggests), but rather will make the compiler infer the type at the call point.

If you change the typedef , instead of receiving an error message indicating that the helperFn arguments cannot be matched with an existing function, the type will be inferred and the function will match, but you will get an error deeper in helperFn if you use a property of the type that no longer present. Or, even worse, you cannot even get an error message if it is semantics of a changed type.

Note that typedef has the value std::list<X> , and in the function that you execute with this simple correct loop:

 for (typename T::iterator it=x.begin(), end=x.end(); it != end; ) { if ( condition(*it) ) it = x.erase(it); else ++it; } 

Can you catch an effect that changes the typedef type to std::vector<X> ? The compiler cannot even if the code is now incorrect. Regardless of whether the for loop entry is similar, is it a good idea or why is it not just using the delete-delete idiom - these are different problems (in fact, the previous cycle is probably better than delete-delete for a list), the specific situation is that the semantics have changed, and since the type is syntactically compatible with the previous one, the compiler will not notice that the code is incorrect, it will not point you to this function, and most likely you will not revise / rewrite it.

+3
source

I guess this is the idea of generic programming - to do things with the Foo part without knowing its type. A more "traditional" (strongly typed, boring, readable, duplicate code - you name it) method would have to explicitly indicate the type:

 int helperFn(const std::blahblah& foopart, int index) { ... return foopart.fn(index); } 
+1
source

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


All Articles