Ambiguous function call when Reference and Value overload exist - c++

I have the following class:
template<typename T>
class List {
void Add(T& item) {//GOOD STUFF}
void Add(T item) {//More STUFF}
void Remove(T item) {//STUFF}
};
I am trying to use it like the following
List<MyClass> list;
MyClass obj;
list.Add(obj); //Here the compiler gets angry :((
Regarding the question I have already found the following three SO questions, but I am still not able to call either of the methods.
Ambiguous call with overloaded r-value reference function
function call ambiguity with pointer, reference and constant reference parameter
Ambiguous Reference/Value Versions of Functions

It is ambiguous which function you intend to call, because any l-value being passed as argument to a function can be implicitly converted to a reference, so the ambiguity is unavoidable, as said in Function Overloading Based on Value vs. Const Reference.
You could change this:
void Add(T item) {}
to this:
void Add(T&& item) {}
Live demo

Related

I have Question about class overriding in C++

When I study about override keyword, I found some strange thing like below code.
#include <iostream>
template <class T>
class A
{
public:
virtual void some_function(const T a)
{
std::cout<<__PRETTY_FUNCTION__<<std::endl;
std::cout<<"Base"<<std::endl;
}
};
class Derived : public A<int*>
{
public:
virtual void some_function(const int* a)
{
std::cout<<__PRETTY_FUNCTION__<<std::endl;
std::cout<<"Derived"<<std::endl;
}
};
int main()
{
A<int*>* p = new Derived;
p->some_function(nullptr);
delete p;
}
When I first saw that code, I expected "Derived" to be called.
But above code print result like below.
void A<T>::some_function(T) [with T = int*]
Base
But when I removed const keyword in the some_function that placed in Derived class,
class Derived : public A<int*>
{
public:
virtual void some_function(int* a)
{
std::cout<<__PRETTY_FUNCTION__<<std::endl;
std::cout<<"Derived"<<std::endl;
}
};
It print "Derived".
Can you tell me why this is happening?
The function prototypes are not the same. For T=int* const T a: a is a const pointer to an int, while const int *a is a pointer to an const int. For one the pointer is const, for the other the int is const.
int * const a would be the same as const T a, or you can make T=const int*.
https://godbolt.org/z/WEaEoGh58
Also important to note: When you derive from a class you must declare a virtual destructor.
A hint: when you use the keyword override on the derived function, you will get an error that you are not overriding the function: https://godbolt.org/z/o87GMrhsd
At
const T a
const qualifier is interpreted as top-level cv-qualifier for T, so it is processed
T const a
thus if int* is set to T, parameter type becomes
int* const a
So, your Derived's function signature (it is called second level const qualifier)
const int* a
is different from Base's one. As a result, your Derived virtual function is not called.
In addition, C++ has the rule of "ignore top-level cv-qualifier from function signature". So, your Base's signature
int* const
and if you give the type of
int*
into Derived's signature, these two types signature are interpreted to be equivalent. As a result, your Derived virtual function is called correctly.
The following is a back-ground for your help. The type of
T*
has two component T and pointer and then
T* const
is called top-level cv-qualifier which is applied to *, and then
T const *
is called second level cv-qualifier which is applied to T (equal to const T*). C++ ignore top-level cv-qualifier from signature. On the other hand, second level qualifier is included to signature. Therefore for example,
void f(int*) {}
void f(int* const){}
is failed to compile because of same signature by top-level cv-qualifier ignoring. In contrast,
void f(int*) {}
void f(int const * ){}
is compiled successfully by the difference of second level cv-qualifiers.
This is important to understand the behavior of virtual function calls. The reason is that the signature of derived virtual function and base's one must be identical to call it correctly.

Deleting all rvalue function overloads of a class

Say I have a class object that must be captured by the caller when returning this class's object from a function call.
// no_can_rvalue *must* be captured
[[nodiscard]] no_can_rvalue a_func();
I can enforce this by deleting all rvalue function overloads, thus making it impossible to use the class functionality unless a caller has captured an object of it (doubled with nodiscard in c++17).
Is it possible to delete all rvalue function overloads of a given class in one fell swoop?
The result being equivalent to :
struct no_can_rvalue {
void f() && = delete;
void f() &;
void g() && = delete;
void g() &;
// etc
};
No, it is not possible to do so.

Reference or pointer to std::vector of incomplete type

As answered here: How can an incomplete type be used as a template parameter to vector here? usage of incomplete type as template argument when instantiating a template component can result in undefined behaviour. But does that rule hold true when we have only pointer/reference to template component with incomplete type as argument? Does the instatiation happen in this case too?
For example:
// SomeAlgoInterface.hpp
#include <vector>
struct Result; // forward declaration
class SomeAlgoInterface
{
public:
virtual ~SomeAlgoInterface() = default;
public:
// the following line is definitely OK, ...
virtual void f1(const Result & result) = 0;
// ... but I'm not quite sure about the following one
virtual void f2(const std::vector<Result> & results) = 0;
};
In other words, is the code above valid or not?
The declaration is correct, so long as you don't call f2.
The compiler doesn't need to know the internal of Result class if you don't call f2. There's no storage allocation in the declaration.
If some compilation unit call f2, you need to supply the complete type for Result class, or you need another reference parameter to call f2:
void another_f(SomeAlgoInterface& i, std::vector<Result>& results)
{
i.f2(results);
}

Cannot call a method of const reference parameter in C++

class A
{
public:
A(){};
~A(){};
void method(){};
};
void call(const A &a)
{
a.method(); // I cannot call this method here if I use "const" but I can call it if not using "const"
}
int main()
{
A a;
call(a);
return 0;
}
In this case, the error is: "passing const A as this argument of void A::method() discards qualifiers [-fpermissive]|"
In function call, if I use const, I get the error, but if I get rid of it, it works.
Can anyone explain it for me?
You can't call non-const member functions via const references. You can fix this by making the member function const:
void method() const {};
^^^^^
This indicates that calling the member does not mutate the object it is called on*
* Conceptually. In practice it can mutate members marked mutable

How does calling this function object work?

I have a class
class fobj{
public:
fobj(int i):id(i) {}
void operator()()
{
std::cout<<"Prints"<<std::endl;
}
private:
int id;
};
template<typename T>
void func(T type)
{
type();
}
If I invoke func like
Method 1:
func(fobj(1));
the message I wanted to print is printed.
I was always thinking I needed to do something like
Method 2:
fobj Iobj(1); // create an instance of the fobj class
func(Iobj); // call func by passing Iobj(which is a function object)
How does Method 1 work? I mean what exactly happens?
And how is a call made to the operator() in class fobj ?
One thing to note is that this works because your template class is taking an object by value:
template<typename T>
void func(T type) // this takes a T by value
...
because of this, it can take either an lvalue (such as an actual variable) or an rvalue (such as the temporary).
If for some reason you did want to limit func to only taking an lvalue, you could modify the function to using pass by reference:
template <typename T>
void func(T &type) // this takes a T by reference
...
using pass by reference does allow the side effect of the function being able to modify the object.
In func(fobj(1)), fobj(1) creates a temporay fobj from the literal int 1. This temporary is used to initialized the function parameter type (there's an implicit copy which the compiler may elide), and in the body of the function operator() is invoked on the function object.
I think that naming the function parameter type is a bit misleading. type is the name of the T instance (in this case a fobj) that is the function parameter.