Passing function with default parameter value as template parameter - c++

I am a bit puzzled with the following behavior. I pass a function with two parameter, one having a default value, as a template parameter and call the function with one argument. Why does it fail to compile? And, what is the solution/workaround?
#include <iostream>
using namespace std;
template<typename Function>
void eval(Function function) {
function(10);
}
void sum(int i, int j = 0) {
cout << "Sum is " << i + j;
}
int main() {
sum(10); // OK, of course :)
eval(sum); // Error!
}
Note that this question is not about calling a templated function with default parameter.
The error message:
prog.cpp: In instantiation of 'void eval(Function) [with Function = void (*)(int, int)]':
prog.cpp:15:10: required from here
prog.cpp:6:10: error: too few arguments to function
function(10);
^

That's because the optional parameter is part of function declaration. When you call using a function pointer, essentially all the compiler knows is the type of the function pointer. So this line function(10) roughly translates to:
void (*)(int, int) sm = function; // the equivalent happens at the type deduction step
sm(10); // whoops, where is the second int
The compiler will need the second argument because it has no way of knowing whether sm is pointing to sum which has a default argument, or some other void foo(int a, int b) which doesn't have a default argument.

This is why function pointers are weak. You got two good answers of why this doesn't work - the function template doesn't know your function has a defaulted argument.
The solution is to not pass a function pointer - pass in a function object. In this case, you can handwrite a lambda:
eval([](int i){ return sum(i); });
which seems like a really annoyingly verbose way of basically just writing sum in a way that actually works. But this sort of thing comes up periodically (what if sum were an overloaded name?) so it's handy to just have a lambdafying macro around (C++14 required):
#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(auto) { \
return func(std::forward<decltype(args)>(args)...); }
so you can write:
eval(AS_LAMBDA(sum));
That will work with defaulted arguments, overloaded names, and even work to bind member functions:
struct X {
int i;
int add(int j) { return i + j; }
};
X x{42};
eval(AS_LAMBDA(x.add));

When you call sum:
sum(10);
compiler translates it to:
sum(10, 0);
There is no error, as arguments are matched. For the compiler there exist no sum taking one argument. You can say a pre-compiler doing the magic of passing 0 to second argument to sum. With templates this magical pre-compiler doesn't exist, and the actual compiler matches sum with two strictly two arguments, which it doesn't find, and hence the error.
Just like
void foo(int);
void foo(float);
will fail to compile with foo(10.2); where argument double can be changed to either float or int. The compiler will not assume float for you. Similarly, compiler will not try to find best match of sum just for the sake of successful compilation.

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

Lambda as template function

I have a very strange problem. To keep things simple, lets say I want to have a function which takes 2 functions with the same declaration as arguments
template<typename Func>
void foo(Func a, Func b)
{
std::cout << "good";
}
To try things out I took putchar from cstdio, and created an identical function to match the putchar.
int myPutcharFunc(int)
{
return 0;
}
int main()
{
auto myPutcharLambda = [](int) -> int
{
return 0;
};
foo(putchar, myPutcharFunc); // okay
foo(putchar, myPutcharLambda); //deduced conflicting types for parameter 'Func' ('int (__attribute__((__cdecl__)) *)(int)' and 'main()::<lambda(int)>')
}
Now, the lambda does not want to compile (the key is I want to use lambda capture).
So lets add template specialization, because the programmer is wiser than the machine, right? :)
template<typename Func>
void foo(Func a, Func b)
{
std::cout << "good";
}
template<>
void foo(int(*)(int), int(*)(int))
{
std::cout << "good";
}
No luck, the same error - why?
But for some reason, when I comment out the template specialization:
//template<>
void foo(int(*)(int), int(*)(int))
{
std::cout << "good";
}
The code compiles. I obviously do not want to overload foo for EVERY set of function's arguments - thats what templates are for. Every step was tested both with msvc++ and g++. What am I doing wrong?
Two possibilities.
1: Just put + in front of the lambda:
foo(putchar, +myPutcharLambda);
That works because unary + expects an integer-like value, such as a pointer. Therefore, the lambda converts to a function pointer.
Ultimately a (non-capturing) lambda doesn't have the same type as a function pointer, even though it's willing to convert to a function pointer.
How is a compiler supposed to know which conversions are allowed to make two objects of the same type?
2: There is another option, making use of the fact that the ?: is willing to do some conversions, converting one type to another in some circumstances.
template<typename Func1, typename Func2>
void foo2(Func1 a, Func2 b)
{
using common_type = decltype(true?a:b); // or 'false', it doesn't matter
foo<common_type>(a,b);
}
Every lambda is a different type, so you'll need to have two different template parameters to get them
template<typename FuncA, typename FuncB>
void foo(FuncA a, FuncB b)
Types don't decay when deducing template types (SEE COMMENT FOR CORRECTION). So a lambda remains a lambda and doesn't decay to a function pointer. Same reason a string literal is deduced as a char[N] instead of a const char *.
With your second example using specialization, it doesn't want to use your specialization, since the lambda is not a function pointer. You can cast the Lambda to a function pointer and make it work: https://godbolt.org/g/ISgPci The trick you can do here is say +my_lambda because + is defined for pointers so it will force the non-capturing lambda to become a function pointer.
A lambda has its own type which can decay to a function pointer but not in the case of a template function match, it will for the real function as you found because of the implicit conversion.
In the case of matching to a template you need to disambiguate and explicitly instantiate foo with the type you want or convert the lambda to a function pointer.
foo<decltype(putchar)>(putchar, myPutcharLambda);
or
foo(putchar, +myPutcharLambda);

I want to know distinction between functions taking const or nonconst parameters

As I have seen from C++ Primer,fourth edition,
"What may be surprising, is that although the parameter is a const inside the function, the
compiler otherwise treats the definition of fcn as if we had defined the parameter as a plain int:"
void fcn(const int i) { /* fcn can read but not write to i */ }
void fcn(int i) { /* ... */ } // error: redefines fcn(int)
So, if I want to define two functions as follows,
int func(const int i) {
return i;
}
int func(int i){
i++;
return i;
}
Actually,they are two different functions,but when I compile it,there is a message:error:redefinition of 'int func(int i)'.Can I define them like this? Is there any alternative methods?
Parameter passed by value (like your int) are copied onto the stack,
then if your function takes a 'const int' as an argument, it does not change what can be done from outside the function (it justs forbids you from changing the value inside the function). Since the parameter is copied, a function that takes an int as an argument can be called on a const int (it will not modify the argument since it gets a copy).
Since there is no difference 'seen from outside' of the function between the one that takes an int and a const int, there would be no reason for the compiler to choose one version or the other, this is why the C++ norm states that it is the same function.
Now if you have a function that takes a reference f(int &) and another one that takes a const reference f(const int&), it is another story: then only the second one can be called with a const int argument like in:
const int x=5;
f(x);
const just modifies how you access your variable it doesn't change its data type, as far as the compiler is concerned both of them are integers and that's how they are stored whether modifiable or not.
The only workaround i see is to overload the function by changing the data type of your parameter, maybe long, double, byte etc..

Function overloading with ellipsis

Can i actually use a function overloading like this:
#include <iostream>
void foo(...)
{
std::cout << "::foo(...) \n";
}
void foo(int)
{
std::cout << "::foo(int) \n";
}
int main()
{
foo(0);
foo('A');
foo("str");
foo(0, 1);
}
What standard says about it? And in what kind of situations i'll get ::foo(...)?
void foo(int)
will accept one argument of type int.
void foo(...)
accepts any number of arguments, of any type. It will be selected when the call doesn't have a single int argument. Not very useful, in general.
Also note that it is undefined behavior to pass objects of class type to ....
In N3337 I can see:-
13.3.2 Viable functions
A candidate function having fewer than m parameters is viable only if
it has an ellipsis in its parameter list (8.3.5). For the purposes of
overload resolution, any argument for which there is no corresponding
parameter is considered to “match the ellipsis” (13.3.3.1.3) .
When you declare a function in the following way :
void foo (...)
this mean foo accepts any number of arguments.
So this function will be called when this is the must suitable one.
In your case whenever you won't write :
foo(//Some single int).
In your specific main, this will happen :
foo(0) //Calls foo(int).
foo('A) //Calls foo (int). as you can convert a char to an int.
foo("str") //Calls foo(...) . as you can not convert a string to an int.
foo(1,2) //Calls foo(...) . as this is the only possible function
cause the second foo function only takes one int.
void foo(...) will take variable arguments. And it will be called when there the no or type of argument does not match the provided argument list of other function with the same function name.
foo(0); //This will call void foo(int) function
foo('A'); //This will call void foo(int) function
foo("str"); //This will call void foo(...) function
foo(0, 1); //This will call void foo(...) function
NOTE:
Although the ellipsis works fine with function overloading, it is not highly recommend pursuing variadic functions. At least not until you have significantly more experience in C++ to understand the pitfalls. I would suggest its use only with the try catch block where there are situations when the error cannot be predicted.

Calling a static function from a template parameter in a lambda function while ignoring default parameters

Ok, I'm using Visual Studio 2010 to mess with lambdas in templates. VC++ has an odd quirk dealing with template parameters, but I found a workaround for calling a static function of a template parameter (T::magic in this example) by using the auto keyword. However, I have hit another hitch.
Say I have one of many classes with a function "magic", and only the first 2 parameters matter for my call later. It may or may not have some defaulted parameters after the first 2. I don't really care about them for this application, I only need to call magic(start, otherthing). Uhoh is one of these classes:
struct Uhoh
{
static int magic(char* start, int otherthing, bool doom = false);
}
I do the call with this template. I have to use a workaround of using the auto type to get T::magic for it to work in the lambda function.
template<typename T> void example()
{
auto themagic = T::magic;
std::function<int (char*)> test = [=](char* start) -> int
{
return themagic(start, 0);
};
test(0);
}
And then I call it or whatever.
int main()
{
example<Uhoh>();
}
I get an error about "too few arguments for call", even though "magic" can take two arguments. Now, I can't know for sure what the type will be for "otherthing" in any of the various "magic" functions. All I know is that 0 will be a valid value. Passing the type of whatever otherthing will be to the function "example" will be an extreme annoyance at best.
How can I properly type "themagic" so that VC++ doesn't throw an error?
auto themagic = T::magic;
Here the information that the third parameter of T::magic has default value is lost, as the type of themagic is deduced as int (*)(char*, int, bool), which cannot have any default value for the third parameter. So you cannot call themagic with only two arguments. You've to pass third argument as well.
So do this:
std::function<int (char*)> test = [=](char* start) -> int
{
return themagic(start, 0, false);
};
Please note that default-value for a function parameter is not a part of the function signature, which means when you write
auto themagic = T::magic;
then type of themagic cannot be deduced as int (*)(char*, int, bool=false).