Advertisement

C++ Overloading Question

Started by August 27, 2017 10:45 AM
19 comments, last by Oberon_Command 7 years, 3 months ago

Hi guys. I recently studied C / C++ Coding Standards book.


//Function overloading must be avoided in most cases
//Use:
const Anim* GetAnimByIndex(const int index) const;
const Anim* GetAnimByName(const char* name) const;
//Instead of:
const Anim* GetAnim(const int index) const;
const Anim* GetAnim(const char* name) const;

I found this, and I'm really curious why function overloading must be avoided in most cases.

I want to hear exact answer about it. Thx.

The authors may have some more or less rational arguments against overloading.

In this particular case, the call site may be a bit unexpected if you use NULL or 0 as a null pointer constant, as the integer overload will be chosen.

There's nothing intrinsically wrong with overloading, but it can be a bit more clear at the call site what function is intended and what it does.

Use common sense and adhere to local style.

To make it is hell. To fail is divine.

Advertisement

Thanks! I thought there was a great reason about this issue.

Most people's coding styles swing on a pendulum over their careers. You'll go through phases of liking different things.

Some people might like the symmetry, simplicity and terseness of the 2nd option. Other people might like the verbose, explicitness of the 1st option.

Arguments about readability are very subjective... but personally I'd choose the first option right now as I feel like it improves readability of the algorithm at the call site. Also, I would expect the performance characteristics of those two functions to be completely different to each other (find by index should be almost free, but find by name is hopefully a binary search of string comparisons or a string hash, etc...). Personally I believe you should not write code where using the slow path and using the fast path will look the same. It should be obvious when reading the calling code whether they're doing something dumb (repeatedly doing unnecessary string comparisons) or not.

1 hour ago, Zao said:

In this particular case, the call site may be a bit unexpected if you use NULL or 0 as a null pointer constant, as the integer overload will be chosen.

If you want to avoid this, you need to use "nullptr" instead of avoiding overloads. This, however, depends on the language standard you use which for most books is unfortunately still below C++11/14.

🧙

The only time overloading becomes very tricky and problematic is in the case of universal reference templates.


template< typename T > 

void Func(T &&arg);

This becomes even more tricky with variadic template arguments.


template< typename... T >

void Func(T&&... args);

Which becomes even more tricky with constructors. ;)

In all other cases, overloading shouldn't be a big deal.

🧙

Advertisement

If you pass primitive types by value to a function const is not needed on that parameter. The code outside wont see any changes you make to the parameter anyway.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, theHunter, theHunter: Primal, Mad Max, Watch Dogs: Legion

This C++ coding style guideline is NOT about readability.  Not if you mean making people THINK they like reading the code.  This is about true readability, accuracy, debug-ability.  The overload based version reads easier ... you don't think about the function as 2 functions, and you don't have to think about the type.  However, these methods are actually 2 different lookups, semantically, so they should have 2 different names - If I converted the int 5 to the string "5" and called the method, it would NOT return the animation with the index 5, but instead of return a null pointer because there is no animation with the NAME "5".  As C++ has evolved with all sorts of anonymous and auto typing, this kind of thing would be almost 100% invisible to a reader and even tough for a debugger.

There is nothing wrong with overloads like in the math library:

float sqrt(float)

double sqrt(double)

because they are semantically identical.  Never mix (basic) TYPE selection with semantic selection.

There are reasons to violate every "rule".  But in general only violate rules like this in methods are only FOR violating this rule.  in other words a method with overloads that change the behavior based on the type of the input, should ONLY be a facade for selecting different behaviors based on type 

On 8/27/2017 at 5:49 AM, Hodgman said:

Personally I believe you should not write code where using the slow path and using the fast path will look the same. It should be obvious when reading the calling code whether they're doing something dumb (repeatedly doing unnecessary string comparisons) or not.

While I agree with the idea of "you should not write code where using the slow path and using the fast path will look the same", I'm not sure it applies that much here.  The two functions still take two different arguments, one an index and one a string.  So looking at the code it should be clear that you're calling the one that takes the string, and hence would be expected to be slower.  Adding "ByName" to the end of the function only mostly succeeds in making the function name longer and more redundant, since the input type already tells you the same information.

1 hour ago, 0r0d said:

Adding "ByName" to the end of the function only mostly succeeds in making the function name longer and more redundant, since the input type already tells you the same information.

I guess it depends what your naming convention for variables is. If you're using some horrible systems hungarian where you attach the data type to the variable name, then sure, you'd have something redundant like:


GetAnimByName( pszMyAnimation1 ); // slow string-based logic
GetAnimByIndex( uidxMyAnimation2 ); // fast index-based logic

But if your variable is just called "myAnimationN", then the call-site could look like:


GetAnim( myAnimation );//is this the fast path or the slow path?

 

This topic is closed to new replies.

Advertisement