C++: Weird behavior on method overloading - c++

I need explanation about why the following code does not compile. I have a workaround which I will articulate below, but I don't understand the failure of the original version.
To speed up code reading: The concept is to define an interface (ISomething), then to create an abstract implementation (ASomething) which implements the second function (2) using the first (not yet defined) one (1). A complete implementation which derives from the abstract one (for example SomethingImpl) must define the first method and has the option to override the second one.
#include <iostream>
class ISomething
{
public:
virtual ~ISomething()
{ }
virtual int f(int x) = 0; // (1)
virtual int f(int x, int y) = 0; // (2)
};
class ASomething
: public virtual ISomething
{
public:
virtual int f(int x, int y) // (2)
{
return f(x) + f(y); // (3)
}
};
class SomethingImpl
: public ASomething
{
public:
virtual int f(int x) // (1)
{
return x+1;
}
};
int main()
{
SomethingImpl a;
std::cout << a.f(10) << std::endl; // (1)
std::cout << a.f(10,20) << std::endl; // (2)
return 0;
}
Compiling this code gives error on both Visual Studio 2013 (Windows) and g++ 4.4.5 (Linux). The errors are very similar, I will detail the g++ output only:
$ g++ SibFun.cpp -o SibFun
SibFun.cpp: In member function ‘virtual int ASomething::f(int, int)’:
SibFun.cpp:18: error: no matching function for call to ‘ASomething::f(int&)’
SibFun.cpp:16: note: candidates are: virtual int ASomething::f(int, int)
SibFun.cpp:18: error: no matching function for call to ‘ASomething::f(int&)’
SibFun.cpp:16: note: candidates are: virtual int ASomething::f(int, int)
SibFun.cpp: In function ‘int main()’:
SibFun.cpp:36: error: no matching function for call to ‘SomethingImpl::f(int, int)’
SibFun.cpp:26: note: candidates are: virtual int SomethingImpl::f(int)
make: *** [SibFun] Error 1
I tried to use different notation at (3) like return this->f(x) + this->f(y), but I experienced no significant change in the error message.
However when I changed (3) to return ISomething::f(x) + ISomething::f(y); I only got:
$ g++ SibFun.cpp -o SibFun
SibFun.cpp: In function ‘int main()’:
SibFun.cpp:36: error: no matching function for call to ‘SomethingImpl::f(int, int)’
SibFun.cpp:26: note: candidates are: virtual int SomethingImpl::f(int)
make: *** [SibFun] Error 1
But! When change (2) from f to g all compiles and runs as expected.
What is the reason behind this begavior? Why can't I use the f name for (2)?

Function overloading works only for functions visible in the same scope:
class ASomething
: public virtual ISomething
{
public:
virtual int f(int x, int y) // (2)
{
return f(x) + f(y); // (3)
}
using ISomething::f;
//~~~~~~~~~~~~~~~~~^
};
class SomethingImpl
: public ASomething
{
public:
virtual int f(int x) // (1)
{
return x+1;
}
using ASomething::f;
//~~~~~~~~~~~~~~~~~^
};

Both compilation failures happen for the same reason. While you're overriding one virtual member function, you're hiding the other. In ASomething:
virtual int f(int x, int y) // (2)
{
return f(x) + f(y); // (3)
}
Name lookup on f finds ASomething::f and stops, it doesn't keep going to look for other overloads. Since ASomething::f takes two arguments, and you're trying to call it with one, error. In order to allow for overloading against the base class, you have to introduce the base class member functions with a using-declaration:
using ISomething::f; // NOW, ISomething::f(int ) is found by lookup
virtual int f(int x, int y)
{
return f(x) + f(y);
}
And similarly, SomethingImpl needs a using ASomething::f; statement so that a.f(10) can compile.

The problem is not an 'overloading' problem, but hiding a base class function (See other answers)
A slightly modified example:
#include <iostream>
class ISomething
{
public:
virtual ~ISomething() {}
virtual int f(int x) = 0; // (1)
virtual int f(int x, int y) = 0; // (2)
};
class ASomething: public virtual ISomething
{
public:
virtual int f(int x, int y) // (2)
{
// `this->f(x)` fails:
ISomething& i = *this;
return i.f(x) + i.f(y); // (3)
}
};
class SomethingImpl: public ASomething
{
public:
virtual int f(int x) // (1)
{
return x+1;
}
};
int main()
{
SomethingImpl a;
// `a.f(10, 20)` fails:
ISomething& i = a;
std::cout << i.f(10) << std::endl; // (1)
std::cout << i.f(10, 20) << std::endl; // (2)
return 0;
}
Hence, calling f from the interface, resolves the conflict. Although, you should consider using base::f, as suggested in other answers.

Related

cppcheck: one definition rule is violated when overriding

In the following code (which is a minimal example based on a much more complex code), a base class defines a struct local to the class. A derived class overrides this definition, but uses also the definition in the base class.
#include <iostream>
struct base {
struct update;
void apply(int& x);
};
struct base::update {
void operator()(int& x)
{
++x;
}
};
void base::apply(int& x) { update{}(x); }
struct deriv : public base {
struct update;
void apply(int& x, int& y);
};
struct deriv::update {
void operator()(int& x, int& y)
{
typename base::update{}.operator()(x);
++y;
}
};
void deriv::apply(int& x, int& y) { update{}(x, y); }
int main()
{
base b;
int x = 1;
b.apply(x);
std::cout << x << std::endl;
int y = 2;
deriv d;
d.apply(x, y);
std::cout << x << ' ' << y << std::endl;
return 0;
}
Compiling with gcc and -Wall -Wextra does not issue any warning.
The output of the code is as expected
2
3 3
However, when analyzing the code with cppcheck, with --enable=all, I get the following:
Checking test.cpp ...
test.cpp:25:24: style: Parameter 'x' can be declared with const [constParameter]
void operator()(int& x, int& y)
^
test.cpp:9:1: error: The one definition rule is violated, different classes/structs have the same name 'update' [ctuOneDefinitionRuleViolation]
struct base::update {
^
test.cpp:24:1: note: The one definition rule is violated, different classes/structs have the same name 'update'
struct deriv::update {
^
test.cpp:9:1: note: The one definition rule is violated, different classes/structs have the same name 'update'
struct base::update {
The first reported error (Parameter 'x' can be declared with const) seems clearly a false positive, as clearly I need non-const reference to be able to modify it, and in fact the code obviously does not compile with a const int&.
But what about the second error on ODR violation? Is this a correct diagnose, and if so why cannot I override the definition of update?
It is also a false positive. There is nothing wrong with declaring a nested class of the same name in the derived class. The two nested classes will be separate entities and each can have one definition per one-definition-rule.

Confused in output of a program

I'm new to c++ and i'm practicing about class. kindly someone help me with this. What's the error?
class Test1 {
int y;
};
class Test2 {
int x;
Test1 t1;
public:
operator Test1() { return t1; }
operator int() { return x; }
};
void fun ( int x) { };
void fun ( Test1 t ) { };
int main() {
Test2 t;
fun(t);
return 0;
}
When compiling :
t.cc: In function ‘int main()’:
t.cc:18:10: error: call of overloaded ‘fun(Test2&)’ is ambiguous
fun(t);
^
t.cc:13:6: note: candidate: void fun(int)
void fun ( int x) { }
^~~
t.cc:14:6: note: candidate: void fun(Test1)
void fun ( Test1 t ) { }
^~~
To solve that you have to indicate your choice :
fun(static_cast<Test1>(t));
or
fun(static_cast<int>(t));
or of course to remove one of the conversion operator
It's a Compiler Error.
There are two conversion operators defined in the Test2 class. So Test2 objects can automatically be converted to both int and Test1. Therefore, the function call fun(t) is ambiguous as there are two functions void fun(int ) and void fun(Test1 ), compiler has no way to decide which function to call. In general, conversion operators must be overloaded carefully as they may lead to ambiguity.

C++ member function with auto and default arguments

I am trying to implement a member function in a C++ class which has an auto argument (a lambda) and an int argument with a default value. Something like this:
class Base {
public:
int add_one(auto fobj, int b=3);
};
int Base::add_one(auto add_fcn, int b) {
return add_fcn(1, b);
}
However, a simple test like this fails to compile:
#include <iostream>
class Base {
public:
int add_one(auto fobj, int b=3);
};
int Base::add_one(auto add_fcn, int b) {
return add_fcn(1, b);
}
int main() {
int ans;
auto add_fcn = [](int a, int b) -> int {return a + b;};
Base obj;
ans = obj.add_one(add_fcn);
std::cout << ans << "\n";
return 0;
}
The error the compiler (MinGW 7.2.0, flags: -std=c++14) gives me is the following:
error: call to 'int Base::add_one(auto:2, int) [with auto:1 = main()::<lambda(int, int)>]' uses the default argument for parameter 2, which is not yet defined
I sincerely do not understand the error. Can someone please explain me the reason of this error and how it can be fixed? Thank you in advance.
auto parameters is a gcc extension. It means that it is not a standard compliant way to solve the problem.
I am not sure what is the exact reason for the error above, but you might achieve the same effect with template member function which works well:
class Base {
public:
template<typename F>
int add_one(F fobj, int b = 3);
};
template<typename F>
int Base::add_one(F add_fcn, int b) {
return add_fcn(1, b);
}
Wandbox example
Another possible way is to use std::function (which implies some performance overhead though):
class Base {
public:
int add_one(std::function<int(int, int)> fobj, int b = 3);
};
int Base::add_one(std::function<int(int, int)> add_fcn, int b) {
return add_fcn(1, b);
}
Wandbox example
Finally, you could make use of pointers to functions, but it is too C way...
If you'd like to expand your knowledge on passing functions to functions, this article by Vittorio Romeo gives an excellent explanation + some benchmarks.

Using a custom constructor as a template function

I have template function change that takes a function that takes int and returns an object of type A. So I thought I can use the constructor of A
class A {
int y;
public:
explicit A(int y) : y(2 * y) {
}
};
class B {
A x;
public:
B(int x) : x(x) {
}
template<typename F>
void change(int y, F func) {
x = func(y);
}
};
int main(void) {
B b(7);
b.change(88, A()); // << here
return 0;
}
But the compiler says no matching function for call to ‘A::A()’
How can I make it works?
You can't pass a constructor as a parameter like you are attempting. The C++ standard is very strict on not allowing the memory address of a constructor to be taken.
When you call change(88, A()), you are actually constructing a temp A object (which the compiler should not allow since A does not have a default constructor) and then you are passing that object to the parameter of change(). The compiler is correct to complain, since A does not define an operator()(int) to satisfy the call to func(y) when called in an A object.
To make this work, you need to create a separate function that constructs the A object, and then pass that function to change(), eg:
A createA(int y)
{
return A(y);
}
int main(void) {
B b(7);
b.change(88, createA);
return 0;
}

Is this function overloading correct?

Consider having these four functions in one C++ program:
void a(int val)
{
cout<<val;
}
void a(int &val)
{
cout<<val;
}
void a(int *val)
{
cout<<val;
}
void a(double val)
{
cout<<val;
}
Few Questions i have are:
Is there going to be any error in the code? or are they all overloaded without any error.
Can you tell me how to call all of these four functions correctly? My try was below:
int iTmp;
int *pTmp;
double dTmp;
a(iTmp);
a(iTmp);
a(pTmp);
a(dTmp);
The only problem are the functions:
void a(int &val)
and
void a(int val)
The compiler will create the following errors:
Compilation error time: 0 memory: 3140 signal:0
prog.cpp: In function 'int main()':
prog.cpp:28:8: error: call of overloaded 'a(int&)' is ambiguous
a(iTmp);
^
Because he cant distinguish both, if you remove one of them the compilation succeeds
See Example:
#include <iostream>
using namespace std;
void a(int val)
{
cout<<val;
}
void a(int *val)
{
cout<<val;
}
void a(double val)
{
cout<<val;
}
int main() {
int iTmp = 0;
int *pTmp = 0;
double dTmp = 0.0;
a(iTmp);
a(iTmp);
a(pTmp);
a(dTmp);
return 0;
}
See working example:
http://ideone.com/WRZUoW
Your code will compile but calling
int iTmp;
a(iTmp);
will yield an ambiguous overload resolution call since both
void a(int val)
{
cout<<val;
}
void a(int &val)
{
cout<<val;
}
will match.
The reason for this lies in the standard:
[over.match.best]
A viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2)
int->int
int->int&
these are standard conversion sequences and the entire list of [over.ics.rank]p3.2 is checked without success for a better conversion sequence (exact match in both cases)
They are thus deemed "undistinguishable".
A word of advice: the variable are also not initialized and (if automatic variables) even if your code compiled, the output would be undefined.
The compiler must be able to know what function to call.
With
int i = 12;
a(i);
both a(int i) and c(int& i) are acceptable candidates and compiler throws an error.
and
void a(const int& i) { ... }
will suffer from same problem.
These two function will cause issues
void a(int val)
{
cout<<val;
}
void a(int &val)
{
cout<<val;
}
Your call a(iTmp); have two possible candidates. Compiler should shown an error for these. Otherwise pointer argument(int *val)and double argument(double val) are okay.
The most obvious problem is that
void a(int val) {
and
void a(int& val) {
are ambiguous when called with iTmp.
Aside from that, depending on whether the variables are global or local ones, the code is using uninitialized variables -> UB and whether or not,
void a(int* val) {
std::cout << val << std::endl;
}
is overloaded correctly is up for debate. I'd assume that it should be std::cout << *val << std::endl;, but that of course depends on what the function is supposed to do (although a function that prints an address would usually be parameterized by (void* or char*);