I just wanted to know whether is this following block of code fully valid in C++:
class A
{
public:
virtual bool b() = 0;
};
class B
{
public:
virtual bool b() = 0;
};
class C: public A, public B
{
public:
virtual bool A::b()
{
return true;
}
virtual bool B::b()
{
return false;
}
};
Using VS2008 it compiles without any errors, however, on GCC (MinGW) 3.4.5 it gives me errors like:
cannot declare member function `A::b' within `C'
On the lines where the virtual methods are implemented. I was curious if this is just generally considered invalid and by C++ standards forbidden code (and in VS it thus works thanks to some MS non-standardized magic), or only a bug or unsupported language feature in GCC.
No it's not valid. You can't override them separately like that, because they would have the same signature.
There is a guru of the week about that.
The qualified name A::b is not allowed as the name of a member of class C.
It should not compile due to parents having the same function name. Furthermore, you aren't suppose to overload the function b more than once.
If you refer to the virtual table created for this:
0(beginning of vtable) - A::b() -> B::b()
You see, since class B has the same function name as class A, it overrides it, thus you have now B::b() to override (since it's still pure). This is due to multiple inheritancy. How would the compiler be able to differenciate between the two (they have the same signature)? Generally, this will fail, because like I just said, the compiler isn't suppose to make decisions, it's supposed to tell you that there is a problem.
It compiles on VS, but have you tried running it (included in a file where it is actually created)? Sometimes, compiler are lazy and don't pop errors on classes that aren't used.
Just as an FYI, VC gives the error only when you try to use the b method:
C:\temp\test.cpp(33) : error C2668: 'C::b' : ambiguous call to overloaded function
C:\temp\test.cpp(23): could be 'bool C::b(void)'
C:\temp\test.cpp(18): or 'bool C::b(void)'
while trying to match the argument list '(void)'
And for what it's worth, Comeau's compiler behaves similarly, but with an even more confusing error message:
"C:\temp\test.cpp", line 33: error: no instance of overloaded function "C::b"
matches the argument list
object type is: C
bool z = c.b();
Related
I am unable to understand the output of the following C++ snippet.
Should not objc.fn() call A's fn() since B's fn() is private and should not be visible in C. However, the answer is: the call to fn() is ambiguous. How?
#include<iostream>
using namespace std;
class A{
public:
void fn() { cout << "1"; }
};
class B{
void fn() { cout << "2"; }
};
class C: public A, public B {};
int main(){
C objc;
objc.fn();
return 0;
}
According to the book C++ Templates: The Complete Guide Appendix B.1,
At a very high level, a call to a named function can be processed in
the following way:
The name is looked up to form an initial overload set.
If necessary, this set is tweaked in various ways (for example,
template deduction occurs).
Any candidate that doesn't match the call at all (even after
considering implicit conversions and default arguments) is
eliminated from the overload set. This results in a set of so-called
viable function candidates.
Overload resolution is performed to find a best candidate. If there
is one, it is selected; otherwise, the call is ambiguous.
The selected candidate is checked. For example, if it is an
inaccessible private member, a diagnostic is issued.
As you can see, the access authority is checked at last, so the call to fn() is ambiguous will be reported firstly at step #4.
Consider this:
class D {
void fn1() {}
public:
void fn2() {}
};
int main() {
D d;
d.fn2(); // Compiles fine
d.fn1(); // Error: fn1 is private
}
Notice that the error for d.fn1() is about fn1 being private, not that fn1 is unknown or invisible in this scope. By the same token, B::fn is indeed visible in C.
We get this behavior because the compiler does name resolution the same for public and private members. Only after the name has been resolved does the accessibility check come into play.
It is the same for your case: Which fn do you mean? Only after you have answered that question can the compiler tell you if you have access to that fn or not.
As I suspected, this code gives an ambiguous reference and does not compile. As you have declared both functions with the same signature, the compiler doesn't know which of the functions to select, giving a complaint at compilation time.
$ make pru
g++ pru.cc -o pru
pru.cc: In function ‘int main()’:
pru.cc:15:8: error: request for member ‘fn’ is ambiguous
pru.cc:9:9: error: candidates are: void B::fn()
pru.cc:6:9: error: void A::fn()
make: *** [pru] Error 1
I think you can select which function to use, placing the A::fn() selector. Even if, as it is the case in this example, one of the functions is private, you have two functions to select from (you could make the call from inside a B method and you'll get the same error ---in that case you have visibility to both methods).
The correct answer has already been given by #songyuanyao.
Checking access specifiers at the last was designed by the C++ Standard Committee.
Had it not been so, the following things would have occurred :
If one modifies their code in future and change the access specifier of their functions, it may break the compilation of the dependent classes.
More horrible yet, you may start calling a totally unrelated function, and would remain oblivious..!!
Let us assume that access specifiers prevent a function from participating in overload resolution, then :
class A{
public:
void fn() { cout << "1"; }
};
class B{
void fn() { cout << "2"; }
};
class C: public A, public B {};
int main(){
C objc;
objc.fn(); // A::fn() is being invoked
return 0;
}
Now we modify the code:
class A{
void fn() { cout << "1"; }
};
class B{
public:
void fn() { cout << "2"; }
};
Nothing else has been touched, and suddenly
objc.fn(); // B::fn() is being invoked
The caller of your function has no idea that his underlying function is not the same anymore.
This is blasphemy !!
To prevent all such mishaps from happening, the Standard Committee took this design decision.
I'm getting the following compile error in one of my classes, using gcc 3.4.5 (mingw):
src/ModelTester/CModelTesterGui.cpp:1308: error: request for member `addListener' is ambiguous
include/utility/ISource.h:26: error: candidates are: void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SConsolePacket&]
include/utility/ISource.h:26: error: void utility::ISource<T>::addListener(utility::IListener<T>*) [with T = const SControlPacket&]
Hopefully you can see that ISource<T> is a template interface that just indicates that the object can be an informer for an object that is of some matching type IListener<T>. So the thing that has me irked is this idea that for some reason functions are ambiguous when, as far as I can tell, they are not. The addListener() method is overloaded for different input types IListener<const SConsolePacket&> and IListener<const SControlPacket&>. The usage is:
m_controller->addListener( m_model );
Where m_model is a pointer to an IRigidBody object, and IRigidBody inherits only from IListener< const SControlPacket& > and definately not from IListener< const SConsolePacket& >
As a sanity check, I used doxygen to generate the class hierarchy diagram and doxygen agrees with me that IRigidBody does not derive from IListener< const SConsolePacket& >
Evidently my understanding of inheritence in c++ is not exactly correct. I'm under the impression that IListener<const SControlPacket&> and IListener<const SConsolePacket&> are two different types, and that the function declarations
addListener(IListener<const SConsolePacket&>* listener)
and
addListener(IListener<const SControlPacket&>* listener)
declare two separate functions that do two separate things depending on the (distinct) different type of the parameter that is input. Furthermore, I'm under the impression that a pointer to an IRigidBody is also a pointer to an IListener<const SControlPacket&> and that by calling addListener( m_model ) the compiler should understand that I'm calling the second of the above two functions.
I even tried casting m_model like this:
m_controller->addListener(
static_cast<IListener<const SControlPacket&>*>(m_model) );
but still get that error. I cannot for the life of me see how these functions are ambiguous. Can anyone shed light on this issue?
P.S. I know how to force the function to be un-ambiguous by doing this:
m_controller->ISource<const SControlPacket&>::addListener( m_model );
I just happen to think that is terribly unreadible and I would prefer not to have to do that.
Edit... just kidding. That apparently doesn't fix the problem as it leads to a linker error:
CModelTesterGui.cpp:1312: undefined reference to `utility::ISource<aerobat::SControlPacket const&>::addListener(utility::IListener<SControlPacket const&>*)'
Looks like your situation is like this:
struct A {
void f();
};
struct B {
void f(int);
};
struct C : A, B { };
int main() {
C c;
c.B::f(1); // not ambiguous
c.f(1); // ambiguous
}
The second call to f is ambiguous, because in looking up the name, it finds functions in two different base class scopes. In this situation, the lookup is ambiguous - they don't overload each other. A fix would be to use a using declaration for each member name. Lookup will find names in the scope of C and don't lookup further:
struct C : A, B { using A::f; using B::f; };
Now, the call would find two functions, do overload resolution, and find that the one taking int will fit. Carried over to your code, it would mean that you have to do something like the following
struct controller : ISource<const SConsolePacket&>, ISource<const SControlPacket&> {
using ISource<const SConsolePacket&>::addListener;
using ISource<const SControlPacket&>::addListener;
};
Now, the two names are in the same scope, and now they can overload each other. Lookup will now stop at the controller class, not diving further into the two base-class branches.
When I compile and run this with Visual C++ 2010:
#include <iostream>
int main() {
int subtrahend = 5;
struct Subtractor {
int &subtrahend;
int operator()(int minuend) { return minuend - subtrahend; }
} subtractor5 = { subtrahend };
std::cout << subtractor5(47);
}
I get the correct answer, 42.
Nevertheless, the compiler complains that this is impossible:
Temp.cpp(9) : warning C4510: main::Subtractor : default constructor could not be generated
Temp.cpp(6) : see declaration of main::Subtractor
Temp.cpp(9) : warning C4512: main::Subtractor : assignment operator could not be generated
Temp.cpp(6) : see declaration of main::Subtractor
Temp.cpp(9) : warning C4610: struct main::Subtractor can never be instantiated - user defined constructor required
What's going on?
The first two warnings are just letting you know that the implicitly declared member functions cannot be generated due to the presence of a reference data member.
The third warning is a Visual C++ compiler bug.
All three warnings can be ignored with no ill effects, though you can easily make all three go away by making the reference data member a pointer instead (reference data members are almost never worth the trouble).
The first warning is to tell you that a reference value cannot be defaultly constructed(references are guaranteed to point to some value). Switch the subtrahend to a regular integer and the problem will go away.
I am pretty sure the second warning is of similar nature.
(Just saying, it is generally much better to rely on something like boost::function or a similar implementation(std::tr1::function?) instead of writing this code manually)
It's because the variable subtractor5 is an unnamed struct. If you want to make the errors go away, give the structure used for subtractor5 a name.
For example:
struct subtractor {
:
} subtractor5 = { subtrahend };
I unfortunately don't know enough C++ language-ese to know why it works, but I do know why the warning happens.
A user defined constructor is mandatory in following cases:
Initializing constant data members (const int c_member;).
Initializing reference data members (int & r_member;)
Having a data member whose type doesn't have default constructor. Eg:
class NoDefCtor
{
public:
NoDefCtor(int);
};
class ContainThat
{
NoDefCtor no_ctor_member;
};
Inheriting from a base class, where base class doesn't have default constructor. Almost same as above (NoDefCtor).
Say you have a sub class B which inherits from super class A. You want a function that can accept either A or B.
template <typename T>
void someFunc(T* pObj, bool someOtherArg)
{
pObj->AnInheritMethod();
if (pObj->IsASub())
{
pObj->ANonInhertMethod();
}
}
When I compile this (Visual Studio 6) I get:
error C2065: 'pObj' : undeclared identifier
Am I way off base here?
You don't need a function template for this; the following will do just fine:
void someFunc(A* pObj)
{
pObj->AnInheritMethod();
if (B* pObjAsB = dynamic_cast<B*>(pObj))
{
pObjAsB->ANonInheritMethod();
}
}
Or, if you prefer to use your IsASub() member function instead of dynamic_cast:
void someFunc(A* pObj)
{
pObj->AnInheritMethod();
if (pObj->IsASub())
{
B* pObjAsB = static_cast<B*>(pObj);
pObjAsB->ANonInheritMethod();
}
}
Aside from the missing return type, I don't see anything obviously wrong with the code in your example; I don't have Visual C++ 6 installed to check.
You don't need templates for that, that is a free behaviour courtesy of polymorphism.
Edit: also if you write something like:
if (pObj->IsASub())
then there's maybe something wrong in your design. The method is supposed to work for any type in the derivation chain.
You're asking a question that is completely unrelated to the code and error that you included.
In order to have a function take an A or a class derived from A, all it needs to do is take a pointer or reference to A, e.g.
someFunc(A* pObj, bool someOtherArg);
or
someFunc(A& obj, bool someOtherArg);
It will work by virtue of inheritance. That's kind of the whole point of deriving classes from each other. The way you have written it with templates, it will work with any class that defines the three methods you use, whether or not it derives from A.
Now the error you posted is unrelated to this question but is bizarre. There's nothing wrong with the code you posted, but Visual Studio 6 is an ancient compiler; it's twelve years old and does not fully support modern ISO standard C++. This error may be an artifact of a sub-standard templating implementation in the compiler.
I have a templated class that performs an action on the class that is given as template argument. For some of my classes I want to 'group' the functionality in one class, to make it easier for the caller. In fact the code looks something like this (names were changed):
template<typename T>
class DoSomeProcessing
{
public:
process(T &t);
};
class ProcessingFrontEnd : public DoSomeProcessing<CustomerOrder>, public DoSomeProcessing<ProductionOrder>
{
};
The problem is that when I call ProcessingFrontEnd::process with a CustomerOrder as argument, that the compiler complains about it.
I tried to reproduce the problem in a smaller test application. This is the code:
#include <vector>
class X : public std::vector<char>
, public std::vector<void *>
{
};
int main(void)
{
X x;
x.push_back('c');
return 0;
}
And indeed, if this is compiled, Microsoft's VS2010 compiler gives this error:
test.cpp
test.cpp(11) : error C2385: ambiguous access of 'push_back'
could be the 'push_back' in base 'std::vector<char,std::allocator<char> >'
or could be the 'push_back' in base 'std::vector<void *,std::allocator<void *> >'
test.cpp(11) : error C3861: 'push_back': identifier not found
I tested this test application with different types (char+void*, double+void*) and different arguments in the call ('c', 3.14), but the error message is always the same.
I tested this with VS2005 and VS2010 but I always get the same error.
Why can't the compiler determine the correct function to call? What makes this confusing for the compiler? Or is it just a bug in the Microsoft compiler?
EDIT:
If I explicitly add 2 push_back methods to my class, like this:
class X : public std::vector<char>
, public std::vector<void *>
{
public:
void push_back(char c) {}
void push_back(void *p) {}
};
The compiler doesn't complain anymore. So with these methods he can clearly distinguish between a character and a void-pointer. Why can't he do this if the two push_back methods are inherited from the parent?
This is by design. The compiler is not trying to resolve overloaded
functions because these are not overloaded
functions. The standard is really clear on that
(see 10.2.2). If the same name is found in two
different bases, it's an ambiguity, even if they
could be resolved correctly with the call (i.e. in
your case). Same-named functions in different classes will typically have quite different purposes and hence the selection between them should not be made on the basis of
their arguments. There are many good reasons not to
allow that, but here's one.
Imagine your class C derives from A and B and
these two base classes come from two different
libraries. If the author of B adds a new function
to the class, it may break the user's code by
redirecting a call from A::foo() to B::foo() if
the latter is a better match.
If you want the two functions to be treated in the same way that they would
be if part of a single class, then the best way to do it is with using
declarations in the derived class. Just add
using std::vector<char>::push_back;
using std::vector<void *>::push_back;
to the declaration of class X.
I believe you are running afoul of the C++ overloading rules which prohibit overloading across classes. You'd get the same results if your template classes were two separate classes, each with its own process(CustomerOrder) and process(ProductionOrder) member.
The workaround is explicit using statements inside your derived class, pulling in each overload from each of the template base classes.
How is the compiler supposed to know which process you want to call? There's two options. Do you want both, one, or the other?
You need to override process in the derived class.