Default argument vs std::optional as an argument vs Function Overloading - c++

Let's say I'm implementing a function func.
I have a use case where func will be invoked with two parameters.
I have another use case where func will be invoked with just one parameter but a default value of the missing second parameter will be used within the function.
I can think of 3 possible ways of implementing this functionality:
Default Argument
void func(int a, long b = c);
func will be invoked like this:
func(a);
func(a, b);
Function Overloading
void func(int a);
void func(int a, long b);
func will be invoked like this:
func(a);
func(a, b);
Using Optional as a function argument
void func(int a, optional<long> b);
func will be invoked like this:
func(a, optional<long> ());
func(a, b);
I want to know what's the best way of implementing the desired functionality. Any suggestions are welcome.

There are two questions to ask yourself when considering this:
Does the optional argument have a logical default?
Is the function implemented the same when the optional argument is given and when it's not?
If the second argument has a logical default and the function does the same thing no matter what, then a default argument works well. For example:
std::vector<std::string> split_string(const std::string& str, char sep = ' ');
By default, this splits a string on spaces, but the separator can be given to change that. It makes little sense to split this into two overloads or use a std::optional here.
If the second argument doesn't have a logical default but the function is still mostly the same if it isn't given, then std::optional makes more sense. For example:
void extract(const std::filesystem::path& archive_file,
const std::filesystem::path& output_dir,
std::optional<std::regex> exclude_filter = {});
Here we're extracting files from an archive file and writing the extracted files to disk, optionally excluding files that match some pattern. The function definition would be fundamentally the same with or without the filter; it's just one extra line difference:
if (exclude_filter && std::regex_match(file, *exclude_filter) continue;
It doesn't make much sense to duplicate the definition, so overloading doesn't really make sense. At the same time, there's no "don't match anything" regex, so there's not a logical default filter that could be applied. std::optional is a perfect fit here.
Note that I did still use a default argument. Defaulting your std::optional to be empty can make your calls much nicer.
Lastly, if the function implementation is fundamentally different between the one-arg and two-arg versions, then use an overload. For example:
void hide(window& win) {
win.set_visible(false);
}
template <typename rep, typename period>
void hide(window& win, const std::chrono::duration<rep, period>& fade_time) {
auto wait_time = equal_intervals(fade_time, win.opacity());
while (win.oapcity() > 0) {
win.set_opacity(win.opacity() - 1);
std::this_thread::sleep_for(wait_time);
}
}
Here we're hiding a window with an optional fade out time. While the functions logically do the same thing, they're implemented totally differently. Being the same function doesn't make a lot of sense. Even though 0 would be a logical default fade time, it still doesn't make sense to use a default argument here since you would just end up with a big if block:
if (fade_time == 0) {
// body of the first overload
} else {
// body of the second overload
}

Related

Multiple functions with the same name but their parameters are either constant or received by value or by reference

The title is a bit lengthy, but it's best explained by an example:
Suppose we have the following functions in C++:
void SomeFunction(int num) { //1
}
void SomeFunction(int& num) { //2
}
void SomeFunction(const int& num) { //3
}
void SomeFunction(const int num) { //4
}
All of these are called the same way:
SomeFunction(5);
or
int x = 5;
SomeFunction(x);
When I tried to compile the code, it rightfully says more than one instance of overloaded function "SomeFunction" matches the argument
My question is: Is there a way to tell the compiler which function I meant to call?
I asked my lecturer if it was possible, and she tried something along
SomeFunction< /*some text which I don't remember*/ >(x);
But it didn't work and she asked me to find out and tell her.
I also encounter this post:
How to define two functions with the same name and parameters, if one of them has a reference?
And it seems that 1 and 2 can't be written together, but what about 3 and 4? Can either one of those be called specifically?
1 and 4 have the same signature, so you'll need to drop one of those.
The other functions cannot be called directly, but you could add a template function that allows you to specify the desired parameter type:
template<class Arg>
void Call(void f(Arg), Arg arg)
{
f(arg);
}
// Driver Program to test above functions
int main()
{
int i;
Call<int>(SomeFunction, 1);
Call<int&>(SomeFunction, i);
Call<const int&>(SomeFunction, 1);
}
Alternatively you could use a function pointer to choose the signature.
int i;
static_cast<void(*)(int)>(&SomeFunction)(1);
static_cast<void(*)(int&)>(&SomeFunction)(i);
static_cast<void(*)(const int&)>(&SomeFunction)(1);
It would be preferrable to avoid this scenario though and only define overloads for either references or the signature void SomeFunction(int).
Note:
SomeFunction<some text which I don't remember>(x);
only works for template functions and SomeFunction is not a template function, so this is not an option here.
You can HACK it, and I mean it - it's not a good solution to your problem, by static casting your function explicitly:
static_cast<void(*)(int)>(SomeFunction)(i);
static_cast<void(*)(int&)>(SomeFunction)(i);
static_cast<void(*)(const int&)>(SomeFunction)(i);
Demo
It will work for first 3 overloads. 4th one is equivalent to 1st: quote from the standard [over.load]:
Parameter declarations that differ only in the presence or absence of const and/or volatile are
equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when
determining which function is being declared, defined, or called
and there is an example:
int f (int);
int f (const int); // redeclaration of f(int)
Also note that you cannot call 2nd overload with rvalue (temporary).
The only way I see this working the way your lecturer tried is if SomeFunction is a template and these four overloads are specializations.
template<typename T>
void SomeFunction(T num);
template<>
void SomeFunction<int>(int num) {}
template<>
void SomeFunction<int&>(int& num) {}
template<>
void SomeFunction<const int&>(const int& num) {}
template<>
void SomeFunction<const int>(const int num) {}
Then you can call it as follows.
SomeFunction<int>(x);
SomeFunction<int&>(x);
SomeFunction<const int&>(x);
SomeFunction<const int>(x);
Demo
However, this is incredibly stupid in this context. There are a lot of things wrong with the original overloads.
In the 4th one, the const is completely useless from the caller's perspective, because you can call it the same way you can call the 1st, and the argument is a copy anyway. The const only makes it so the argument is constant inside the function. Moreover, the 1st and 4th overloads cannot both be defined at the same time: the const is actually ignored in the prototype and it leads to a redefinition.
The 3rd overload is also useless because a const int& argument provides no benefit over a int argument. In fact, the compiler probably optimizes that away. The only difference is in the scenario I describe at the end. Of course, if the argument type is more complex (not just int or some other fundamental type), it often makes sense.
The 2nd overload is the only one that can modify the variable you pass as argument. However, if the 1st (or 4th, since it's really the same) overload is present as well, you cannot call the 2nd directly because the call would be ambiguous. You could still call the 1st with an rvalue (basically a literal or an expression like std::move(x)).
If the 2nd and 3rd overloads are the only ones present, then there is no ambiguity and you can call the 2nd with non-const lvalues and the 3rd with const lvalues or rvalues.
Demo

Is forwarding of all callables still not possible in C++20?

I would like to make a universal wrapper function which can accept any
free standing or static function,
member function ( perhaps as a specialization with the first argument used as *this)
including overloaded or templated cases together with variable arguments. Such wrapper will then, in the body, call the function exactly with the forwarded parameters.
Example:
template<typename Fnc,typename...Args>
void wrapper(Fnc fnc, Args&&...args){
// Do some stuff
// Please disregard the case when return type is void,
// that be SFINAED with std::result_of.
auto res = fnc(std::forward<Args>(args)...);
// Do some stuff
return res;
}
#include <vector>
auto foo(int i){ return i; }
auto foo(double& d){ return d; }
auto foo(double&& d){ return d; }
auto foo(const char* str){ return str; }
template<typename...T>
auto generic_foo(T&&...ts){/* ...*/ return /* ...*/; }
template<typename T>
void constrained_foo(std::vector<T>& lref,std::vector<T>&& rref, std::vector<T> value){ /**/}
int main(){
// Basics
wrapper(foo, 1);// foo(int)
wrapper(foo, 1.1); // foo(double&&)
wrapper(foo, "ahoj"); // foo(const char*)
// Conversion must work too
wrapper(foo, 1.1f); // foo(double&&)
wrapper(foo, (short)1); // foo(int)
// Detecting lvalues, rvalues is also a must
double y;
foo(y);
foo(std::move(y));
// Similarly for templates
int x;
std::vector<int> v1, v2, v3;
wrapper(generic_foo, 1, 1.1, "ahoj", x, &x);
wrapper(constrained_foo, v1, std::move(v2), v3);
}
The thing I do find so frustrating about this is that I am supplying all the necessary information to make these calls right next to each other, there is no added ambiguity about what to call, I could call it myself, I could(will) make a macro that can do it, but there is no proper C++ syntax for it AFAIK.
I discovered the need for it while trying to automatically call all my methods in certain "context". But thinking about it more, I believe this could have really widespread usage for <algorithm>, <thread> libraries too. Where you would not have to make one-statement lambdas just to call a function/operator with the lambdas parameters or something captured.
The issue arises in any function accepting another function which will be eventually called together with the passed known parameters.
My attempts
generic_foo can be resolved if the return type is fixed:
template<typename...Args>
void wrapper(void(*f)(Args&&... args) , Args&&...args){
// ...
f(std::forward<Args>(args)...);
}
int main(){
int x;
wrapper(generic_foo, 1, 1.1, "ahoj", x, &x, std::move(x));
}
This works nicely, the return type can maybe be resolved too by some obscure and clever use of std::invoke_result_t, but currently it is a kind of chicken-egg situation with the parameter type. Because the only thing how to resolve the name generic_foo is to force it to decay to a function pointer and then there is no name to put in std::invoke_result_t since the parameter is still being deduced.
This will also work with overloads as long as there is an exact match, so it cannot do conversions.
This approach is as far as I can get without macros when the function name is not known in advance.
If the name of the callable is fixed, there is this frequently-used variation of lambda trick:
template<typename Fnc, typename...Args>
void wrapper(Fnc f , Args&&...args){
// ...
f(std::forward<Args>(args)...);
}
int main(){
int x;
wrapper([](auto&&...args)
{ return generic_foo(std::forward<decltype(args)>(args)...);},
1, 1.1, "ahoj", x, &x, std::move(x));
}
If I add a macro doing exactly this:
#define WRAP_CALL(wrapper_fnc, called_fnc, ...) \
wrapper_fnc([&](auto&&...args) \
{ return called_fnc(std::forward<decltype(args)>(args)...);}, \
__VA_ARGS__ );
int main(){
int x;
WRAP_CALL(wrapper, generic_foo, 1, 1.1, "ahoj", x, &x, std::move(x))
}
I get the least macro-infested working solution I can think of, it works with any callable and any wrappers which can stay proper C++ functions. But I would like macro-less version of this and especially for functions.
So I will probably still use this version, does not seem too unreasonable. Are there any corners cases I should know about?
I did not write a single C++20 concept yet, so I still have very small hope there might be something that can work in that area perhaps? But probably not since std::thread(foo,1); also suffers from it.
So this might sadly require language changes because the name of the overload set or a template cannot currently be passed anywhere, even just as some kind of aggregated type. So perhaps something akin to std::initializer_list class plus its sometimes-magical syntax?
If this is indeed the case, I would gladly accept any answer listing any currently active proposals that might help with this. If there are even any.
I did found N3579 - A Type trait for signatures which could perhaps work together with the function pointer solution if the chicken-egg problem is addressed. But the proposal looks very dead.
The "overloaded or templated cases" are not entities that can be function/template arguments—certain cases where overload resolution can use contextual information notwithstanding. What you mean by foo in your wrapper(foo,…) is little more than a token (in particular, it's an overload set), and the language simply has no way of addressing such an object since it has no ("aggregated"?) type. (Conversely, macros do operate on tokens, which is why they are applicable here.) You seem to know most of this, but it may be helpful to see why it's knowably impossible, and why it's a bad idea to think of an overload set as "a callable". (After all, syntactically a type name is also "callable", but it wouldn't make any sense to pass one as a function argument.)
Even if a call f(g,…) were defined to try each overload of g in turn (which it is, for the narrow purpose of deducing f's template arguments), that wouldn't help for (an overload set containing) a template: there would be no way to even evaluate f's SFINAE conditions given a g template for which a specialization had not yet been chosen.
The standard lambda trick, which you also illustrated, is a way of performing overload resolution with the benefit of the argument list, which is why it's pretty much the only approach that works. There are certainly proposals to automate that process, including the vagaries of SFINAE-friendliness and exception specifications.
Consider this instead:
template <class T>
auto wrapper(T fnc) {
// Do some stuff
auto res = fnc(); // <--- no arguments
// Do more stuff
return res;
}
Since arguments are known at wrapper call time, they can also be bound at wrapper call time.
wrapper([](){ return foo(1); });
wrapper([](){ return foo(1.1); });
wrapper([](){ return foo("ahoj"); });
wrapper([&x](){ return generic_foo(1, 1.1, "ahoj", x, &x); });
You can encapsulate this in a macro, which is of course less than ideal, but at least this one is short and readable.
#define DEFER(x) ([&](){return x;})
wrapper(DEFER(foo(1)));
wrapper(DEFER(foo(1.1)));
wrapper(DEFER(foo("ahoj")));
wrapper(DEFER(generic_foo(1, 1.1, "ahoj", x, &x)));
This doesn't do exactly what you want, for example, in
wrapper(DEFER(foo(bar())))
bar is called late. This is fixable with a bit of syntax:
wrapper([z=bar()](){ return foo(z); });
and of course this can be wrapped in a macro too:
wrapper(DEFER1(foo(z), z=bar()));
although this is getting a bit unwieldy.

Avoid Conversion from single element vector to primitive type

I have a problem with constant single element std::vector when pass through a function. C++ compiler automatically call wrong function when the std::vector variable contain a single element. This is though the policy of C++ design. However is there any explicit method to specify in such a case. Here are the examples of the problems
assume i have two overload functions both have the same name "foo"
void foo(const std::vector<int> A)
{
// do vector operator
printf("vector thing");
}
void foo(int a)
{
// do integer operator
printf("integer thing")
}
In general case both of these functions are called correctly
foo({1,2,3}); // print do vector thing
foo( 3 ); // print do integer thing
however from c++ rule. when call
foo({5}); // print do integer thing ( I want it to call as vector )
one of the methods is to create a variable
std::vector<int> B = { 5 };
in order to solve this problem.
I feel this method is a bit clumsy. Is there any method that can void the compiler to treat {5} as 5 and call foo(int a).
note:
here is the reference that explain what the problem is
c++11 single element vector initialization in a function call
You need another overload, taking std::initializer_list as a parameter:
void foo(std::initializer_list<int> A)
{
foo(std::vector<int>(A.begin(), A.end()));
}
If you always call this function by creating vectors directly with {...}, rather than using std::vector variables, then you can remove std::vector overload completely and operate directly on std::initializer_list.
No, because as of C++17 the rules explained on the linked answer still hold.
You can create a temporary instead of a variable, though.
foo(std::vector{5}); // C++17 with class type deduction
foo(std::vector<int>{5}); // older versions
One way to disambiguate the function call is to make the integer overload a function template:
template <class Int> void foo(Int a)
{
std::printf("generalized (maybe integer) thing\n");
}
This way, the invocation
foo({3});
will consider the non-templated function a better match while foo(3) instantiates and calls the function template. This because {3} is an std::initializer_list<int> with one element in the context of type deduction. You can also redirect to the original foo(int a) function like this:
void fooImpl(int a)
{
std::printf("integer thing\n");
}
template <class Int> void foo(Int&& a)
{
fooImpl(std::forward<Int>(a));
}
This refuses to compile when e.g. calling foo with an argument not convertible to an integer, which might be a desirable usage restriction. Also, it should be very unlikely that you encounter a performance overhead due to the forwarding intermediate function.

How can I detect parameter types of a function passed as a parameter?

Problem
I have to write a function that will be used as:
obj.transform([](x& a, y& b) { ... });
obj.transform([](x& a) { ... });
obj.transform([](y const& a, x& b, z const& c) { ... });
obj.transform([a, b](z const& c) { ... });
...
and inside the function declaration, I need to figure out the type of the arguments passed in.
The body of the function is then in the form (assuming x to be a member object and argfn the function passed in):
if (x.mfn1<std::remove_reference_t<Args>...>())
argfn(x.mfn2<std::remove_reference_t<Args>>()...);
Context
If you are asking yourself, why and you have no idea how this could be useful, or if you think this is an XY problem, then you can find the context right here.
My attempts
Attempt #1
template<typename... Args>
void fn(std::function<void(Args...)>) { ... }
This doesn't work because there's apparently no way to have a conversion between that std::function and any lambda.
Attempt #2
template<typename... Args>
void fn(void(*)(Args...)) { ... }
This works with the first, second and third example above (prepending + on each lambda to force a conversion to pointer to function), but fails on the fourth.
It's impossible. For example, what if the argument is a functor containing a templated (especially variadic) operator()? Or one that is overloaded? What are the "arguments" then?
The core problem here is that you're effectively re-implementing ranges but worse. Just offer plain iterators over the entities and then use std::transform. Or find a range library of your choice.

Setters in template classes

I'm trying to find a good way to write a setter function for a template class. For non-template classes if somehow trivial because the function signature/implementation depends on parameter type. For example if the parameter type is int the next function should be optimal:
void MyClass::Set(int value)
{
myValue = value;
}
If the parameter type is std::vector next implementation should be close to optimal:
void MyClass::Set(std::vector<SomeType> value)
{
std::swap(myValue, value);
}
as the right constructor (move or copy) will be used to construct the function parameter and no unnecessary copying occurs, assuming move constructor cost is negligible.
As you can see both implementations have drawbacks when the type is changed: If the type is changed to std::vector for the first version, at least one unnecessary copy is made increasing the actual cost by a factor of 2 or 3.
If the type changed to int in the second version 2 unnecessary copies are made, increasing the actual cost by a factor of 3.
Can you please give me a good generic implementation for a setter function (maybe with overloads)? It should be optimal/close to optimal for any type used as parameter.
PS: I would prefer to not use std::enable_if to make several type dependent setters as the class will increase dramatically.
You can use a forwarding reference to accept either an rvalue or an lvalue, then forward that to the appropriate move or copy assignment operator:
template <typename T>
void Set(T && value) {
myValue = std::forward<T>(value);
}
You should have 3 overloads.
void Set( T&& t ) {
v = std::move(t);
}
void Set( T const& t ) {
v = t;
}
template<class U>
void Set( U&& u ) {
v = std::forward<U>(u);
}
the first two allow implicit conversion to work well, if the caller uses {} initialization on the argument, or if the caller is the name of a function and it needs a context, or a few other cases: it will work. This fixes some of the bigger annoyances with "perfect forwarding".
The third gives you anything for which your operator= is overloaded to handle that isn't covered by the first two. It is an example of "perfect forwarding", which as the name implies is imperfect.
The most common problem if you only use the 3rd overload, .Set( {construction args} ) doesn't work.
Possibly the const& overload is redundant, but I am unsure.
live example
// and sometimes:
template<class...Us>
void Set( Us&&...us ) {
v = {std::forward<Us>(us)...};
}
The forth is an emplace-set. This is not actually useful in this case (where we are using operator=), but in some cases it can be. For an example of where such an emplace-accessor is useful, optional::value_or should have that overload. Basically, cases where you could directly construct the target value, or where other parameters might lead to you not constructing the value, this is useful: if you do a {std::forward<Us>(us)...} always you might as well use .Set( {args...} ) externally, which calls the first overload above.