Hi i have a confusion or to say more i need to understand something. I have a procedure and another overloaded procedure of same.
string conct (string a, string b) {
string str = conct(a, b, "string");
return str;
}
string conct (string a, string b, const char* c) {
// do the processing;
return concatenated_string;
}
is it possible that instead of having two overloaded functions, if i make c in the overloaded function as default argument. So that even if someone passes only two arguments, i can just have one function to handle that case.
But my main concern comes in the third argument which is currently const char* c. So if i make it to something like const char* c = "string", would it be correct way to handle the case of removing overloading with one function with default argument.
I saw the post here but that seems to be focused on compilation and not the confusion i have.
Yes, you can replace your overloaded functions with one function and a default argument:
string conct (string a, string b, const char* c = "string") {
// do the processing;
return concatenated_string;
}
When you overload functions the compiler generates code for each function, probably resulting in larger code size.
If an overload just acts as a thin wrapper as in your case then the optimizer may eliminate the extra work.
default arguments get set at the caller's location, rather than inside the function, so default arguments must be publicly visible, and changing them requires recompiling all callers. With an overload like yours the psuedo-default argument becomes a hidden detail.
default values can be used in function prototypes but if we want to default middle argument then we'll have to default all values to its right...
On the other hand overloading a function can be done for all possible argument combinations also default value needs not to be placed on function call stack and thus less work for the compiler...
Related
I have a class SpecialString. It has an operator overload / conversion function it uses any time it's passed off as a const char*. It then returns a normal c-string.
class SpecialString
{
...
operator char* () const { return mCStr; }
...
};
This used to work a long time ago (literally 19 years ago) when I passed these directly into printf(). The compiler was smart enough to know that argument was meant to be a char* and it used the conversion function, but the now g++ complains.
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", str1, str2);
error: cannot pass object of non-POD type 'SPECIALSTRING' (aka 'SpecialString') through variadic method; call will abort at runtime [-Wnon-pod-varargs]
Is there any way to get this to work again without changing the code? I can add a deref operator overload function that returns the c-string and pass the SpecialString objects around like this.
class SpecialString
{
...
operator CHAR* () const { return mCStr; }
char* operator * () const { return mCStr; }
...
};
SpecialString str1("Hello"), str2("World");
printf("%s %s\n", *str1, *str2);
But I'd prefer not to because this requires manually changing thousands of lines of code.
You could disable the warning, if you don't want to be informed about it... but that's a bad idea.
The behaviour of the program is undefined, you should fix it and that requires changing the code. You can use the exising conversion operator with static_cast, or you can use your unary * operator idea, which is going to be terser.
Even less change would be required if you used unary + instead which doesn't require introducing an overload, since it will invoke the implicit conversion instead. That may add some confusion to the reader of the code though.
Since you don't want to modify the existing code, you can write a "hack" instead. More specifically, a bunch of overloads to printf() that patch the existing code.
For example:
int printf(const char* f, const SpecialString& a, const SpecialString& b)
{
return printf(f, (const char*)a, (const char*)b);
}
With this function declared in your header, every call to printf() with those specific parameters will use this function instead of the "real" printf() you're familiar with, and perform the needed conversions.
I presume you have quite a few combinations of printf() calls in your code envolving SpecialString, so you may have to write a bunch of different overloads, and this is ugly af to say the least but it does fit your requirement.
As mentioned in another comment, it has always been undefined behavior that happens to work in your case.
With Microsoft CString class, it seems like the undefined behavior was so used (as it happen to work), that now the layout is defined in a way that it will still works. See How can CString be passed to format string %s?.
In our code base, I try to fix code when I modify a file to explicitly do the conversion by calling GetString()
There are a few things you could do:
Fix existing code everywhere you get the warning.
In that case, a named function like c_str or GetString is preferable to a conversion operator to avoid explicit casting (for ex. static_cast or even worst C-style case (const char *). The deref operator might be an acceptable compromise.
Use some formatting library
<iosteam>
fmt: https://github.com/fmtlib/fmt
many other choices (search C++ formatting library or something similar)
Use variadic template function so that conversion could be done.
If you only use a few types (int, double, string) and rarely more than 2 or 3 parameters, defining overloads might also be a possibility.
Not recommended: Hack your class to works again.
Have you done any change to your class definition that cause it to break or only upgrade the compiler version or change compiler options?
Such hack is working with undefined behavior so you must figure out how your compiler works and the code won't be portable.
For it to works the class must have the size of a pointer and the data itself must be compatible with a pointer. Thus essentially, the data must consist of a single pointer (no v-table or other stuff).
Side note: I think that one should avoid defining its own string class. In most case, standard C++ string should be used (or string view). If you need additional functions, I would recommend your to do write stand-alone function in a namespace like StringUtilities for example. That way, you avoid converting back and forth between your own string and standard string (or some library string like MFC, Qt or something else).
I was looking over the following code:
string toUpper(string s) {
string result;
int (*toupperp)(int) = &toupper; // toupper is overloaded
transform(begin(s), end(s), back_inserter(result), toupperp);
return result;
}
I am confused by this line:
int (*toupperp)(int) = &toupper; // toupper is overloaded
1.Why is this line necessary?
2.I believe that & retrieves a pointer to something from memory. But toupper, the name of the function is already a pointer, no? Why can't we do this:
int (*toupperp)(int) = toupper;
3.Why is the function overloaded to int if it's used on a string?
1) It's not necessary, really. If you have used using namespace std directive, it's necessary to cast to the desired type to let the compiler know which overload you want. So, you might also say
transform(begin(s), end(s), back_inserter(result), static_cast<int(*)(int)>(&toupper));
Otherwise the following should be enough:
transform(begin(s), end(s), back_inserter(result), ::toupper);
2) Identifiers that are function names decay into pointers, yes, but they aren't exactly the same thing. That being said, in this case it should be fine to say
int (*toupperp)(int) = toupper;
or even (if you haven't used using namespace std directive):
auto toupperp = toupper;
3) it's for compatibility with C standard library. It's used on every element of s, which for string is a char.
What you are passing to transform is a pointer to the function toupper (see function pointers). You store this pointer into the local variable toupperp. The type of toupperp is a pointer to a function taking an int as argument and returning an int.
Unless toupper is defined in strange ways, the function is seemingly used by transform to change each input character to uppercase. Each single character is processed as an integer (with an implicit cast if needed).
Regarding your question 2, using the operator & you make more explicit you are taking the address of the function, but indeed you could omit it. See here (I learnt something today).
If toupper was overloaded, using the intermediate variable is a safe way to get exactly the desired overload. If the desired overload goes away this method will catch the problem at compile time. See here. (And that's something else I learnt today).
I have code:
int SomeClass::sum(int x)
{
return x+=x;
}
int SomeClass::sum(int & x)
{
return x+=x;
}
....
int num = 0;
int result = sum(num);
that not work. How I can use both functions and indicate which of them I want to use when I сall them?
You can provide two different overloads taking int& and const int&, which might somehow meet your needs...
But the whole code is a bit strange... in the function that takes the argument by value you are modifying it (+=), when it probably makes sense to only read it return x+x;. In the overload that takes the reference, you are both modifying the argument and returning the new value. That is a bit strange.
Other than that, sum is a horrible name for a function that multiplies by 2.
You can not have such functions in C++. They will have to be named differently for instance sumByCopy and sumByRef. How would you expect the compiler to decide which one are you referring to at each point?
You can overload in this way, it will compile OK if you do not call any of them directly. However, it will cause ambiguity when you call sum(num). Therefore, it is no sense to provide those overloads of a function since we cannot directly call none of them. This kind of overloads is useless and violates good practice.
I have a class called FileProc that runs File IO operations. In one instance I have declared two functions (which are sub-functions to operator= functions), both decisively different:
const bool WriteAmount(const std::string &Arr, const long S)
{
/* Do some string conversion */
return true;
}
const bool WriteAmount(const char Arr[], unsigned int S)
{
/* Do some string conversion */
return true;
}
If I make a call with a 'char string' to WriteAmount, it reports an ambiguity error - saying it is confused between WriteAmount for char string and WriteAmount for std::string. I know what is occurring under the hood - it's attempting to using the std::string constructor to implicitly convert the char string into a std::string. But I don't want this to occur in the instance of WriteAmount (IE I don't want any implicit conversion occurring within the functions - given each one is optimised to each role).
My question is, for consistency, without changing the function format (IE not changing number of arguments or what order they appear in) and without altering the standard library, is there anyway to prevent implicit conversion in the functions in question?
I forgot to add, preferably without typecasting, as this will be tedious on function calls and not user friendly.
You get the ambiguity because your second parameter is different. Trying to call it with long x = ...; WriteAmount("foo", x) will raise an ambiguity because it matches the second argument better with the first overload, but the first argument is matched better with the second overload.
Make the second parameter have the same type in both cases and you will get rid of the ambiguity, as then the second argument is matched equally worse/good for both overloads, and the first argument will be matched better with the second overload.
Can't you change the second argument and cast it to unsigned int ? It should not be able to use the first function call. I have not coded in C++ for ages..
I've come across some C++ code that looks like this (simplified for this post):
(Here's the function prototype located in someCode.hpp)
void someFunction(const double & a, double & b, const double c = 0, const double * d = 0);
(Here's the first line of the function body located in someCode.cpp that #include's someCode.hpp)
void someFunction(const double & a, double & b, const double c, const double * d);
Can I legally call someFunction using:
someFunction(*ptr1, *ptr2);
and/or
someFunction(*ptr1, *ptr2, val1, &val2);
where the variables ptr1, ptr2, val, and val2 have been defined appropriately and val1 and val2 do not equal zero? Why or why not?
And if it is legal, is this syntax preferred vs overloading a function to account for the optional parameters?
Yes, this is legal, this is called default arguments. I would say it's preferred to overloading due to involving less code, yes.
Regarding your comment about const, that doesn't apply to the default value itself, it applies to the argument. If you have an argument of type const char* fruit = "apple", that doesn't mean it has to be called with a character pointer whose value is the same as the address of the "apple" string literal (which is good, since that would be hard to guarantee). It just means that it has to be called with a pointer to constant characters, and tells you that the function being called doesn't need to write to that memory, it is only read from.
Yes, the parameters are optional and when you don't pass them, the given default values will be used.
It has some advantages and disadvantages to use default parameter values instead of overloading. The advantage is less typing in both interface and implementation part. But the disadvantage is that the default value is a part of interface with all its consequences. Then when you change the default value, you for example need to recompile a lot of code instead of a single file when using overloading.
I personally prefer default parameters.
I'd like to expand a bit on whether Default Parameters are preferred over overloading.
Usually they are for all the reasons given in the other answers, most notably less boilerplate code.
There are also valid reasons that make overloading a better alternative in some situations:
Default values are part of the interface, changes might break clients (as #Juraj already noted)
Additionally Overloads make it easier to add additional (combinations of) parameters, without breaking the (binary) interface.
Overloads are resolved at compile time, which can give the compiler better optimization (esp inlining) possibilities. e.g. if you have something like this:
void foo(Something* param = 0) {
if (param == 0) {
simpleAlgorithm();
} else {
complexAlgorithm(param);
}
}
It might be better to use overloads.
Can I legally call someFunction using:
someFunction(*ptr1, *ptr2);
Absolutely! Yes, the other 2 variables that the function accepts would have default values you have set in the header file which is zero for both the arguments.
But if you do supply the 3rd and the 4th argument to the function, then those values are considered instead of the default values.