Reputation: 2711
I would like to define as class X with a static method:
class X
{
static string get_type () {return "X";}
//other virtual methods
}
I would like to force classes which inherit from X to redefine the get_type() method and return strings different from "X" (I am happy if they just redefine get_type for now).
How do I do this? I know that I cannot have virtual static methods.
Edit: The question is not about the type_id, but in general about a static method that should be overriden. For example
class X {
static int getid() {return 1;}
}
Upvotes: 5
Views: 1153
Reputation: 5403
Apologies for resurrecting this thread, but I've just encountered this moral crisis as well. This is a very bold and possibly foolish statement to make, but I wholeheartedly disagree with what most people are saying about static virtual
not making any sense. This dilemma stems from how static members are commonly used versus what they're actually doing underneath.
People often express facts using static classes and/or members - something that is true for all instances if instances are relevant, or simply facts about the world in the case of static classes. Suppose you're modelling a Philosophy class. You might define abstract class Theory
to represent a theory which is to be taught, then inherit from Theory
in TheoryOfSelf
, TheoryOfMind
and so on. To teach a Theory, you'd really want a method called express()
which expresses a theory using a particular turn of phrase appropriate to the audience. One would assume that any inheriting class should expose an identical method express()
. If I were able to, I would model this relationship using static virtual Theory.express()
- it is both a statement of fact transcending the concept of instances (therefore static) and nonspecific, requiring a specific implementation by each type of theory (therefore virtual).
I completely agree however with people justifying the prohibition on the grounds of what static
is actually doing - it makes perfect sense in terms of coding principles, the issue arises from the customary ways people commonly model the real world.
The best resolution to this problem I've been able to think of is to model Theory
as a singleton instead - there may be an instance of a theory, but there's only ever one of them. If you want an alternative, it's a different type, so create a new derived class. To me this approach just seems arbitrary and introduces unnecessary noise.
Upvotes: 1
Reputation: 10969
You mention a few places about guaranteeing that the child types yield unique values for your function. This is, as others have said, impossible at compile time [at least, without the use of templates, which might or might not be acceptable]. But if you delay it until runtime, you can maybe pull something similar off.
class Base {
static std::vector<std::pair<const std::type_info*, int> > datas;
typedef std::vector<std::pair<const std::type_info*, int> >::iterator iterator;
public:
virtual ~Base() { }
int Data() const {
const std::type_info& info = typeid(*this);
for(iterator i = datas.begin(); i != datas.end(); ++i)
if(*(i->first) == info) return i->second;
throw "Unregistered Type";
}
static bool RegisterClass(const Base& p, int data) {
const std::type_info& info = typeid(p);
for(iterator i = datas.begin(); i != datas.end(); ++i) {
if(i->second == data) {
if(*(i->first) != info) throw "Duplicate Data";
return true;
}
if(*(i->first) == info) throw "Reregistering";
}
datas.push_back(std::make_pair(&info, data));
return true;
}
};
std::vector<std::pair<const std::type_info*, int> > Base::datas;
class Derived : public Base { };
const DerivedRegisterFlag = Base::RegisterClass(Derived(), 10);
class OtherDerived : public Base { };
const OtherDerivedRegisterFlag = Base::RegisterClass(OtherDerived(), 10); //exception
Caveats: This is completely untested. The exceptions would get thrown before entering main if you do it this way. You could move the registration into constructors, and accept the per-instance overhead of registration checking if you'd rather.
I chose an unordered vector for simplicity; I'm not sure if type_info::before
provides the necessary semantics to be used as a predicate for a map, and presumably you won't have so many derived classes that a linear search would be problematic anyhow. I store a pointer because you can't copy type_info
objects directly. This is mostly safe, since the lifetime of the object returned by typeid
is the entire program. There might be issues when the program is shutting down, I'm not sure.
I made no attempt to protect against static order of initialization errors. As written, this will fail at some point.
Finally, no it isn't static, but "static" and "virtual" don't really make sense together anyhow. If you don't have an instance of the type to act on, then how do you know which overwritten method to chose? There are a few cases with templates where you might legitimately want to call a static method without an actual object, but that's not likely to be common.
*edit: Also, I'm not sure how this interacts with dynamically linked libraries and the like. My suspicion is that RTTI is unreliable in those situations, so obviously this is similarly unreliable.
Upvotes: 1
Reputation: 41331
If I'm not mistaken, to call the static method, you have to invoke the method by specifying the exact name of the class, e.g X::get_type();
, DerivedClass::get_type()
etc and in any case, if called on an object, the dynamic type of the object is not taken into account. So at least in the particular case, it will probably only be useful in a templated context when you are not expecting polymorphic behavior.
However, I don't see why it shouldn't be possible to force each interesting class (inherited or not, since "compile-time polymorphism" doesn't care) to provide this functionality with templates. In the following case, you must specialize the get_type
function or you'll have a compile-time error:
#include <string>
struct X {};
struct Derived: X {};
template <class T> std::string get_type() {
static_assert(sizeof(T) == 0, "get_type not specialized for given type");
return std::string();
}
template <> std::string get_type<X>() {
return "X";
}
int main() {
get_type<X>();
get_type<Derived>(); //error
}
(static_assert
is C++0x, otherwise use your favourite implementation, e.g BOOST_STATIC_ASSERT
. And if you feel bad about specializing functions, specialize a struct instead. And if you want to force an error if someone accidentally tries to specialize it for types not derived from X, then that should also be possible with type_traits
.)
Upvotes: 3
Reputation: 320421
Here you go
class X
{
static string get_type() {return "X"; }
};
class Y : public X
{
static string get_type() {return "Y"; }
};
The code above does exactly what you requested: the derived class redefines get_type
and returns a different string. If this is not what you want, you have to explain why. You have to explain what is it you are trying to do and what behavior you expect from that static method. If is absolutely unclear form your original question.
Upvotes: 1
Reputation: 8720
I'd say you know the why but just in case here's a good explanation:
It looks like your going to have to design your way out of this. Perhaps a virtual function that wraps a Singleton?
Upvotes: 2
Reputation: 308130
template<int id>
class X {
public:
static int getid() { return id; }
};
class Y : public X<2> {
};
You haven't overridden the method, but you've forced every subclass to provide an ID. Caveat: I haven't tried this, there might be some subtle reason why it wouldn't work.
Upvotes: 5
Reputation: 14685
You can't do this for a number of reasons. You can't define the function in X and have it be pure virtual. You can't have virtual static functions at all.
Why must they be static?
Upvotes: 1
Reputation: 490108
To make a long story short, you can't do it. The only way to require a derived class to override a base class function is to make it a pure virtual (which can't be static).
Upvotes: 1