Static, asymmetric or static memberless function?

Every time I have some functions that are in the direction of the "utility", I end up wondering which option is the best. For example, to print message structures (internal or external), some encoding / decoding code, or just some useful conversion functions in the context in which I work.

The options I'm thinking of are as follows:

1) Static function in auxiliary class / structure.

struct helper { static bool doSomething(...); }; 

2) Function without a member.

 namespace helper { bool doSomething(...); } 

3) Static function without a member.

 namespace helper { static bool doSomething(...); } 

In some cases, it may be necessary to initialize or save the state in the "utility", so I go to option 1 to avoid the "global" state. However, if there is no state to be maintained, should I use option 2 or 3? What is the practical difference between options 2 and 3?

What is important to consider and is there a preferred way to approach this? Thanks!

+8
c ++ design static
source share
3 answers

The difference between options 2 and 3 is that in the second case, the function will be internal to the translation unit. If a function is defined only in cpp, it should be an option (which is roughly equivalent to an unnamed namespace, which is the fourth option to consider, again roughly equivalent to 3).

If the function should be used by different translation units, then you should go with option 2. The function will be compiled once (if you do not mark it as inline and provide a definition in the header), and with option 3 the compiler will create an internal copy of it in each translation unit .

As option 1, I would avoid this. In Java or C #, you are forced to use classes everywhere, and you end up with utility classes when operations are not very well matched with the paradigm of the object. On the other hand, in C ++ you can provide these operations as functions that allow you to stand freely, and there is no need to add an additional layer. If you choose a utility class, be sure to turn off object creation.

Whether functions are at the class or namespace level will affect the search and this will affect the user code. Static member functions should always be qualified with the class name, unless you are inside the scope of the class, while there are different ways to include namespace functions in the scope. As an illustrative example, consider a bunch of mathematical helper functions and a call code:

 double do_maths( double x ) { using namespace math; return my_sqrt( x ) * cube_root(x); } // alternatively with an utility class: double do_maths( double x ) { return math::my_sqrt(x) * math::cube_root(x); } 

Which one is easier for you to read is a completely different story, but I prefer the first: inside the function, I can select a namespace and then just focus on operations and ignore the search problems.

+12
source share

Do not use classes as namespaces. Using a free (i.e. non-member) function inside a namespace is the easiest thing.

In addition, a function should not be static if it should be used for several translation units. Otherwise, it will be duplicated.

Using classes as a namespace is stupid: why create a class that you will not create?

If any state needs to be saved, then somewhere there is a global state: a static variable inside the function, a global utility object (possibly in the "private" namespace or as a static global variable in one of your translation units). Do not write classes that you do not create.

Alas, people with a C # / Java background are used to do this stupid thing, but that is because their language developers decided unilaterally that free functions are evil. Whether they should be shot or not is a matter of religion.

As a final word of caution: a global state should be used rarely. Indeed, it associates your code with the existence of a global state in a way that you do not control when the code grows. You should always ask yourself why you are not making this connection explicit.

+8
source share

I use struct with static functions because it offers much better isolation than non-member functions in namespaces (due to avoiding Koenig lookups).

To give an example of the thing I want to avoid:

 namespace Foo { struct A { }; void f(const A& a) {} } void f(const Foo:A& a) { std::cout << "AAA"; } int main(void) { Foo::A a; f(a); // calls Foo:f return 0; } 
-one
source share

All Articles