Could overload happen between template functions? - c++

If I design like this
template<class T>
void func(int i, T t) {
i = i * 2;
cout << "int change into " << i << "when t is " << t << endl;
}
template<class T>
void func(string s, T t) {
cout << "string size is " << s.size() << "when t is " << t << endl;
}
and they works by invoking like
func<int>(1, 1); --------> case 1
func<string>(1, "a"); --------> case 2
func<int>("a", 2); --------> case 3
But in my case, template functions(between case 1 and 3) are used to deal with overload on the first parameter, and function overloads(between case 2 and 3) are in the same use but on the second parameter.
So it seems a bit odd to me that is there any redundancy in my function design?
Is my design valid and efficient?

This design is valid and follows the concept of function overloading in C++. Function overloading allows multiple functions with the same name but with different parameters to exist in a single program.
In this design, the first template function func takes an int and a
generic type T
as parameters, and the second template function takes a string and a generic type T.
It is efficient in that it allows the same function name to be used for different purposes based on the types of the parameters being passed, making the code more readable and maintainable.
However, in case 3, where you are passing a string as the first parameter and an int as the second parameter, both functions are considered equally valid candidates by the compiler, causing an ambiguity error. To resolve this, you may need to either explicitly cast the string to an int or make the func templates more specific to avoid this type of ambiguity.

Related

Passing functions with different number of parameters as argument in C++

[Edited] Based on the feedback from comments, I have rephrased my question.
In C++, is it possible to create a function func that receives another function f as a parameter, if the number of arguments of f might change?
According to this thread, a syntax in C like
void func ( void (*f)(int) );
would help if I already know the number of arguments f receives, but that's not my case.
I want to use func with different functions f1, f2, etc., each of them may or may not have the same number of parameters. For example, f1 could take only one argument, but f2 could take two. That's why I'm trying to look for a way to pass f without specifying the number of arguments. Thanks!
The Q&A you link is for C. In C++ you can pass a function pointer but not always this is the best alternative.
Your question leaves out the crucial part: How do you plan to call f in func when you don't know the number of parameters?
You can do type erasure and remove information of a type easily (eg number of arguments of a function), but latest when you call the function you need to know again how many parameters to pass.
As it isn't perfectly clear what you want to do with the fs that take different number of parameters I will interpret your question like this: Instead of passing functions of different arity to func you can pass a functor with overloaded operator(). Then you can call it with different number of parameters:
#include <iostream>
#include <string>
template <typename F>
void func(F f, int num_params){
if (num_params == 1){
f("Hello");
} else if (num_params == 2) {
f("Hello","World");
}
}
struct functor {
void operator()(const std::string& s){ std::cout << s << "\n";}
void operator()(const std::string& s1,const std::string& s2) { std::cout << s1 << " " << s2 << "\n";}
};
int main() {
func(functor{},1);
func(functor{},2);
}
However, my func is only valid for a functor type that has both, an operator() that can be called with one string literal, and an operator() that can be called with two string literals.

Function overload resolution with nullptr as argument

Consider the code below. Although both overloads of fun accept pointers, passing nullptr to fun does not result in any compilation error. Whereas, the very similar function bun fails to compile. When I print the the types of the argument i using typeid(i).name() (after modifying the code just to get this printed) I get the same type, simply int*. What is the rule that resolves the ambiguity in fun case, but fails for bun? Thanks in advance!
#include <iostream>
struct Foo {
int sth;
};
template< class U>
void fun(decltype(U::sth)* i){
std::cout << "1" << std::endl;
}
template< class U>
void fun(U* i){
std::cout << "2" << std::endl;
}
void bun(decltype(Foo::sth)* i){
std::cout << "3" << std::endl;
}
void bun(Foo* i){
std::cout << "4" << std::endl;
}
int main ( )
{
fun<Foo>(nullptr);
// bun(nullptr); --> call of overloaded 'bun(std::nullptr_t)' is ambiguous
return 0;
}
-----------------------
output : 1
Well, in fact, GCC accepts your code, but Clang does not. So it is not, at first, obvious whether the call is ambiguous.
You ask what rule resolves the ambiguity in the fun case; GCC evidently thinks that there is such a rule. I imagine the rule that GCC is applying is the rule [over.match.best]/1.7 that prefers a more specialized function template over a less specialized one.
The procedure for determining which function template is more specialized than the other is described in [temp.func.order] and is explained thoroughly in this SO answer. However, you will notice that when attempting to apply this procedure to the two overloads of fun as in this question, we run into the problem that the unique synthesized type that needs to be substituted for U in the first overload would need to have a member named sth, and the nature of this member is not specified, and although it may be clear to a human that deduction in the second fun overload must succeed regardless of what sth's type is, the compiler may not be able to prove it.
This is CWG 1157. As this issue is still open with no proposed resolution, I have no insight into whether WG21 intends for this overload resolution to succeed or not.

Function argument binding rules for passing an array by reference vs passing pointer

To prevent any confusion, I very much understand the difference between arrays and pointers, the concept of decay-to-pointer, and the concept of passing an array by reference in C++, etc.
My question here is specifically about the rules used by the compiler to select a function from a set of function overload candidates, when one overload takes an array reference, and the other overload takes a pointer.
For example, suppose we have:
template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
std::cout << "Array-reference overload!" << std::endl;
}
template <class T>
void foo(const T* ptr)
{
std::cout << "Pointer overload!" << std::endl;
}
If we attempt to invoke function template foo() as follows:
const char arr[2] = "A";
foo(arr);
... then my expectation would be that the first overload, the one that takes an array reference, would be selected by the compiler.
However, using GCC 4.9.2, if I compile the above code, I get an error:
test.cpp:28:9: error: call of overloaded ‘foo(const char [2])’ is ambiguous
It's unclear to me why both overloads are considered equally good candidates by the compiler here, since the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.
Now, I am able to get the above overload working by explicitly using type_traits as follows:
template <class T, std::size_t N>
void foo(const T (&arr)[N])
{
std::cout << "Array-reference overload!" << std::endl;
}
template <class T>
void foo(T ptr, typename std::enable_if<std::is_pointer<T>::value>::type* = 0)
{
std::cout << "Pointer overload!" << std::endl;
}
In this case, the program compiles and the overload that takes an array reference is selected. However, I don't understand why this solution should be necessary. I'd like to understand why the compiler considers a function that requires decay-to-pointer an equally likely overload candidate as the array reference, when the argument passed is very much an array.
the first overload matches the type exactly, whereas the second overload requires an extra decay-to-pointer step.
Because when checking the ranking of implicit conversion sequences in overload resolution, the array-to-pointer conversion is considered as an exact match, thus the 2nd overload has the same rank with the 1st one.
From the standard, $16.3.3.1.1 Standard conversion sequences [over.ics.scs] Table 13 — Conversions
Conversion Category Rank Subclause
No conversions required Identity Exact Match
... ...
Array-to-pointer conversion Lvalue Transformation Exact Match [conv.array]
... ...
It's worth noting that the rank of "No conversions required" (i.e. the case for the 1st overload) is "Exact Match" too.

In C++, is it possible to pass in two different data types to a template function?

I'm trying to calculate the absolute value of two numeric values passed in by a user, but allowing the user to enter multiple data types (i.e. an integer and a double, or a char and a float). My initial thought is to use a function sort of like this:
template <class T1, class T2>
void findAbs(const T1& var1, const T2& var2)
{
cout<<"Enter two numbers: "<<endl;
cin>>var1>>var2;
cout<<abs(var1)<<" "<<abs(var2)<<endl;
}
If this is the correct way to do it though, I have no idea how I'd call it in the main function since it seems I'd have to declare the parameters as one data type or another. Any help would be much appreciated!
First off, your example won't compile, because you're taking the parameters by const-reference, and then trying to read into them from the stream. You should take them by non-const reference instead.
With this fixed, you could simply use the function like this:
int main()
{
int i;
float f;
double d;
char c;
findAbs(i, f);
findAbs(c, d);
findAbs(d, i);
//etc.
}
Of course, the type of the arguments must be known in each call site. Templates are a purely compile-time construct. If you were hoping to somehow use the template to differentiate between the end-user typing c, 42 or -3.14, you can't, as that's run-time information.
I have no idea how I'd call it in the main function since it seems I'd have to declare the parameters as one data type or another.
No, template parameters can be deduced in this case. But the main problem is that operator>> of std::cin modifies the parameters, therefore you shouldn't declare them const:
template <class T1, class T2>
void findAbs(T1& var1, T2& var2) {
std::cout << "Enter two numbers: " << std::endl;
std::cin >> var1 >> var2;
std::cout << std::abs(var1) << ' ' << std::abs(var2) << std::endl;
}
Then you should be able to call:
int x, y;
findAbs(x, y);
It might be wise to consider using a single template parameter for both of the arguments though.
Yes, but it depends on whether or not the types you pass in would lead to a piece of code which is valid. Expand the template in your head (or type it out, whatever). Does calling abs on variables of type T1 and T2 make sense? Would it compile? How about the calls to cin? If so, then yes, your code will work.

Overloading resolution

As far as I know, when coming to choose between two candidate functions the compiler will prefer the one that its weakest match is stronger.
For example if I have:
void boo(int i, char c);
void boo(double d, int i);
for the following code:
float f = 1.0;
char c = 'c';
boo(f,c);
the second boo should be prefered because its weakest match is promotion while the first one's is standard type conversion.
But when I try to compile it (using gcc), I get:
error: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second.
Any ideas?
Your understanding of overload resolution is wrong. The general
rule (when there are more than one argument) is to choose
a function for which at least one argument is better (it doesn't
matter how much better), and none of the others are worse. In
other words, the compiler processes each argument separately,
creating a set of "best matches" for it. After this, it takes
the union of these sets: if the intersection contains exactly one
function, you've won. Otherwise, it's ambiguous.
Let's say that you called the function f.
I think that the overload resolution process is:
1. Creating a set of candidate functions. This set of functions includes all of the functions named f that can be accessed from the point where you called f().
2. Creating a set of viable functions. This set of functions is a subset of the candidate functions for which the number of parameters of each viable function agrees with the number of arguments you used to call f().
3. Chooses the best viable function. The best viable function is the one whose parameters all have either better or equal-ranked implicit conversion sequences than all of the other viable functions.
If there are more or less than one (not exactly one), there will be a compilation error.
The following example demonstrates it nicely:
class cat
{
public:
cat(int);
};
void func(int, int, int, cat)
{
std::cout << 1 << std::endl;
}
void func(int, int, double, double)
{
std::cout << 2 << std::endl;
}
int main()
{
func(1,2,3,4);
}
This code generates a compilation error (VS) or warning (g++).
The following will run correctly (print 1):
void func(int, int, int, double)
{
std::cout << 1 << std::endl;
}
void func(int, double, double, double)
{
std::cout << 2 << std::endl;
}
int main()
{
func(1,2,3,4);
}