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.
Related
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.
Problem
I would like to detect if a class has member variables and fail a static assert if they do. Something like:
struct b {
int a;
}
static_assert(!has_member_variables<b>, "Class should not contain members"). // Error.
struct c {
virtual void a() {}
void other() {}
}
static_assert(!has_member_variables<c>, "Class should not contain members"). // Fine.
struct d : c {
}
static_assert(!has_member_variables<d>, "Class should not contain members"). // Fine.
struct e : b {
}
static_assert(!has_member_variables<e>, "Class should not contain members"). // Error.
struct f : c {
char z;
}
static_assert(!has_member_variables<f>, "Class should not contain members"). // Error.
Is there a way to achieve this with SFINAE template? This class may have inheritance or even multiple inheritance with virtual functions (no members in the base classes though).
Motivation
I have a pretty simple setup as follows:
class iFuncRtn {
virtual Status runFunc(Data &data) = 0;
};
template <TRoutine, TSpecialDataType>
class FuncRoutineDataHelper : public iFuncRtn {
Status runFunc(Data &data) {
static_assert(!has_member_variables<TRoutine>, "Routines shouldnt have data members!");
// Prepare special data for routine
TSpecialDataType sData(data);
runFuncImpl(sData);
}
class SpecificRtn :
public FuncRoutineDataHelper<SpecificRtn, MySpecialData> {
virtual Status runFuncImpl(MySpecialData &sData) {
// Calculate based on input
sData.setValue(someCalculation);
}
};
The FunctionalityRoutines are managed and run on a per tick basis. They are customized and can perform a wide variety of tasks such as contacting other devices etc. The data that is passed in can be manipulated by the routine and is guaranteed to be passed in on each tick execution until the functionality is finished. The right type of data is passed in based on the DataHelper class. I wan't to discourage future people from mistakenly adding data to the functionality routines as it is very unlikely to do what they expect. To force this, I was hoping to find a way with static assert.
You can solve this by depending on the compiler doing empty base class optimizations, by checking if a class derived from your T has the same size as an empty class with virtual functions:
template<typename T, typename... BaseClasses>
class IsEmpty
{
// sanity check; see the updated demo below
static_assert(IsDerivedFrom<T, BaseClasses...>::value);
struct NonDerived : BaseClasses... { virtual ~NonDerived() = default; };
struct Derived : T { virtual ~Derived() = default; };
public:
inline static constexpr bool value = (sizeof(NonDerived) == sizeof(Derived));
};
This should work with both single and multiple inheritance. However, when using multiple inheritance, it's necessary to list all base classes, like that:
static_assert(IsEmpty<Derived, Base1, Base2, Base3>::value);
Obviously, this solution rules out final classes.
Here's the updated demo.
Here's the original demo. (doesn't work with multiple inheritance)
You will have to mark the classes in some way or another. Pick a way you are comfortable with, a property or some kind of type integer member with an enum. Whoever makes sub-classes will have to follow your convention to make it work.
All other answers here will be some variant of this.
Any answer that uses a sizeof could not guarantee this will work between platforms, compilers, or even classes on the same platform and compiler, due to easily being able to fit a new member inside the default class member alignment, where the sizes of sizeof could easily end up the same for a sub-class.
Background:
As stated in your code and question, all of that is just plain and basic C ad C++ code, and is resolved entirely at compile time. The compiler will tell you if a member exists or not. After its compiled it's a mash of efficient, nameless, machine code with no hints or help for that kind of thing by itself.
Any name you use for a function or data member effectively disappears, as you know it and see it there, after compile and there is no way to lookup any member by name. Each data member is known only by its numerical offset from the top of the class or struct.
Systems like .Net, Java, and others are designed for reflection, which is the ability to remember class members by name, where you can find them at runtime when you program is running.
Templates in C++, unless mixed mode C++ on something like .Net, are also all resolved at compile time, and the names will also all be gone, so the templates by themselves buy you nothing.
Languages like Objective-C also are written to not fail necessarily if certain types of special members are missing, similar to what you are asking, but under the covers its using a lot of supporting code and runtime management to keep track independently, where the actual function itself and its code are still unware and rely on other code to tell them if a member exists or to not fail on null member.
In pure C or C++ you will need to just make your own system, and be literal about tracking dynamically what does what. You could make enums, or lists or dictionaries of name strings. This is what is normally done, you just have to leave hints for yourself. A class cannot be compiled in a way that gives implicit visibility to future sub-classes by definition, without using some form if RTTI.
Its common to put a type member on a class for this very reason, which could be a simple enum. I would not count on sizes or anything that might be platform dependent.
I have the following class:
template <class T>
class BeliefSet : public Belief<T>
{
private:
std::vector<T> m_Facts;
public:
void RemoveFact(const T Fact)
{
m_Facts.remove(Fact);
}
};
This works fine. However, I want to derive another class from BeliefSet and override this method RemoveFact(), thus I changed the code shown above to the following:
/* Rest of this class has not been changed. */
virtual void RemoveFact(const T Fact)
{
m_Facts.remove(Fact);
}
Now as soon as I compile I get this error:
error C2039: 'remove': is not a member of 'std::vector<std::string,std::allocator<_Ty>>'
Any ideas what I am doing wrong here?
This works fine.
No, it doesn't. std::vector has no member function remove(). However, class template member functions aren't eagerly instantiated. It's likely that you simply never invoked RemoveFact(), so you never had to run into this problem. This "lazy" instantiation is very important - it lets you write and use class templates that have conditionally valid operators without writing loads of SFINAE junk (e.g. I can use std::map fine with non-default-constructive value types, I just can't use operator[]).
When you made the function virtual, as you as inherit from it, it is likely that your compiler attempted to instantiate the function at that point (it is unspecified whether an implementation does so - yours apparently does). Since this function is ill-formed, you get the error via the virtual function instantiation instead of normal function instantiation.
Either way, the function is broken and you want:
void RemoveFact(const T& Fact)
{
m_Facts.erase(
std::remove(m_Facts.begin(), m_Facts.end(), Fact),
m_Facts.end());
}
In C# I can define this:
public interface BaseObject
{
int GetValue();
}
public class Test<T> where T : BaseClass
{
T BaseObject;
}
which means I know that I can alwaysa call BaseObject.GetValue() / BaseObject->GetValue(); because I know that the baseobject has this method.
Is there a similiar way to do this in C++? So that I can define an interface that multiple classes can inherit and a class that can take advantage of this.
Templates, which are even more powerful than C# generics (not to say they are necessarily better, just different).
template<class T>
class foo
{
public:
int whatever()
{
return obj.GetValue();
}
private:
T obj;
};
A separate class is created for each template argument you use. If you provide a template type which would result in an error you will know at compile time.
You're asking about C++ concepts, a way to specify requirements for template parameters. They were proposed during the work on C++11, but proved complicated enough that they weren't done in time. But they've just been delayed, not forgotten.
In the meantime, duck typing remains very powerful, and it will catch when you pass a template parameter that doesn't have the required interface. It just won't report the problem as neatly.
As a workaround, a simple way to check the constraint you showed takes advantage of the fact that pointer conversions are implicit only when upcasting:
public class Test<T> where T : BaseClass
{
static T* enforcement_helper = 0;
static BaseClass* enforce_inheritance_constraint = enforcement_helper;
};
Depending on how new your compiler is, you may need to put those lines inside a special member function (destructor is good, because it's almost always processed).
But you should only check constraints in order to improve error messages (by causing the failure in a clearly commented section of code). C++ templates are duck typed, and they will work with any template parameters that provide the required operations. No formal "interface" is required.
I just don't get it. Tried on VC++ 2008 and G++ 4.3.2
#include <map>
class A : public std::multimap<int, bool>
{
public:
size_type erase(int k, bool v)
{
return erase(k); // <- this fails; had to change to __super::erase(k)
}
};
int main()
{
A a;
a.erase(0, false);
a.erase(0); // <- fails. can't find base class' function?!
return 0;
}
When you declare a function in a class with the same name but different signature from a superclass, then the name resolution rules state that the compiler should stop looking for the function you are trying to call once it finds the first match. After finding the function by name, then it applies the overload resolution rules.
So what is happening is the compiler finds your implementation of erase(int, bool) when you call erase(0), and then decides that the arguments don't match.
1: You need to be extremely careful when deriving from C++ standard library containers. It can be done, but because they don't have virtual destructors and other such niceties, it is usually the wrong approach.
2: Overload rules are a bit quirky here. The compiler first looks in the derived class, and if it finds any overload with the same name, it stops looking there. It only looks in the base class if no overloads were found in the derived class.
A simple solution to that is to introduce the functions you need from the base class into the derived class' namespace:
class A : public std::multimap<int, bool>
{
public:
using std::multimap<int, bool>::erase; // Any erase function found in the base class should be injected into the derived class namespace as well
size_type erase(int k, bool v)
{
return erase(k);
}
};
Alternatively, of course, you could simply write a small helper function in the derived class redirecting to the base class function
You've hidden the base class's erase member function by defining a function in the derived class with the same name but different arguments.
http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9
First of all, you should never derive from STL containers, because no STL containers define a virtual destructor.
Second of all, see Greg's answer about inheritance.
Think whether you really want to inherit from std::map. In all the time I've written code, and that's longer than STL exists, I've never seen an instance where inheriting from a std::container was the best solution.
Specifically, ask yourself whether your class IS a multimap or HAS a multimap.
Others have answered how to resolve the syntax problem and why it can be dangerous to derive from standard classes, but it's also worth pointing out:
Prefer composition to inheritance.
I doubt you mean for 'A' to explicitly have the "is-a" relationship to multimap< int, bool >. C++ Coding Standards by Sutter/Alexandrescu has entire chapter on this (#34), and Google points to many good references on the subject.
It appears there is a SO thread on the topic as well.
For those that use Effective C++ as a C++ programming reference, this issue is covered in Item 33 (Avoid hiding inherited names.) in the book.
I agree with others' comments that you need to be very careful inheriting from STL classes, and it should almost always be avoided.
However, this problem could arise with some other base class from which it's perfectly sensible to inherit.
My question is: why not give your 2-argument function a different name? If it takes different arguments, presumably it has a slightly different meaning? E.g. erase_if_true or erase_and_delete or whatever the bool means.
To replace __super in a portable way, define a typedef at the top of your class like this:
typedef std::multimap<int, bool> parent;
public:
size_type erase(int k, bool v)
{
return parent::erase(k);
}
It does not need to be "parent" of course. It could be any name you like, as long as it is used consistently throughout your project.