C++ 静态虚拟成员?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1820477/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 21:15:46  来源:igfitidea点击:

C++ static virtual members?

c++staticvirtual

提问by cvb

Is it possible in C++ to have a member function that is both staticand virtual? Apparently, there isn't a straightforward way to do it (static virtual member();is a compile error), but is there at least a way to achieve the same effect?

在 C++ 中是否有可能有一个既是staticand的成员函数virtual?显然,没有一种直接的方法可以做到(static virtual member();是编译错误),但是至少有一种方法可以达到相同的效果吗?

I.E:

IE:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

It makes sense to use GetTypeInformation()both on an instance (object->GetTypeInformation()) and on a class (SomeObject::GetTypeInformation()), which can be useful for comparisons and vital for templates.

这是有道理的使用GetTypeInformation()上的一个实例(都object->GetTypeInformation())和一类(SomeObject::GetTypeInformation()),它可以为模板,比较有用和重要。

The only ways I can think of involves writing two functions / a function and a constant, per class, or use macros.

我能想到的唯一方法是为每个类编写两个函数/一个函数和一个常量,或者使用宏。

Any other solutions?

还有其他解决方案吗?

回答by Adam Rosenfield

No, there's no way to do it, since what would happen when you called Object::GetTypeInformation()? It can't know which derived class version to call since there's no object associated with it.

不,没有办法做到这一点,因为当你打电话时会发生什么Object::GetTypeInformation()?它不知道要调用哪个派生类版本,因为没有与之关联的对象。

You'll have to make it a non-static virtual function to work properly; if you also want to be able to call a specific derived class's version non-virtually without an object instance, you'll have to provide a second redunduant static non-virtual version as well.

您必须使其成为非静态虚拟函数才能正常工作;如果您还希望能够在没有对象实例的情况下以非虚拟方式调用特定派生类的版本,则还必须提供第二个冗余静态非虚拟版本。

回答by Rasmus Kaj

Many say it is not possible, I would go one step further and say it is not meaningfull.

很多人说这是不可能的,我会更进一步说它没有意义。

A static member is something that does not relate to any instance, only to the class.

静态成员与任何实例无关,只与类有关。

A virtual member is something that does not relate directly to any class, only to an instance.

虚拟成员不直接与任何类相关,仅与实例相关。

So a static virtual member would be something that does not relate to any instance or any class.

因此,静态虚拟成员将与任何实例或任何类无关。

回答by Nate C-K

I ran into this problem the other day: I had some classes full of static methods but I wanted to use inheritance and virtual methods and reduce code repetition. My solution was:

前几天我遇到了这个问题:我有一些类充满了静态方法,但我想使用继承和虚拟方法并减少代码重复。我的解决方案是:

Instead of using static methods, use a singleton with virtual methods.

不要使用静态方法,而是使用带有虚拟方法的单例。

In other words, each class should contain a static method that you call to get a pointer to a single, shared instance of the class. You can make the true constructors private or protected so that outside code can't misuse it by creating additional instances.

换句话说,每个类都应该包含一个静态方法,您可以调用该方法来获取指向该类的单个共享实例的指针。您可以将真正的构造函数设为私有或受保护,以便外部代码不会通过创建其他实例来滥用它。

In practice, using a singleton is a lot like using static methods except that you can take advantage of inheritance and virtual methods.

在实践中,使用单例很像使用静态方法,只是您可以利用继承和虚拟方法。

回答by Alsk

It is possible!

有可能的!

But what exactly is possible, let's narrow down. People often want some kind of "static virtual function" because of duplication of code needed for being able to call the same function through static call "SomeDerivedClass::myfunction()" and polymorphic call "base_class_pointer->myfunction()". "Legal" method for allowing such functionality is duplication of function definitions:

但究竟有什么可能,让我们缩小范围。人们经常想要某种“静态虚拟函数”,因为能够通过静态调用“SomeDerivedClass::myfunction()”和多态调用“base_class_pointer->myfunction()”来调用相同的函数所需的代码重复。允许此类功能的“合法”方法是重复函数定义:

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

What if base class has a great number of static functions and derived class has to override every of them and one forgot to provide a duplicating definition for virtual function. Right, we'll get some strange error during runtimewhich is hard to track down. Cause duplication of code is a bad thing. The following tries to resolve this problem (and I want to tell beforehand that it is completely type-safe and doesn't contain any black magic like typeid's or dynamic_cast's :)

如果基类有大量静态函数,而派生类必须覆盖它们中的每一个,并且忘记为虚函数提供重复定义怎么办?是的,我们会在运行时遇到一些难以追踪的奇怪错误。因为代码重复是一件坏事。以下尝试解决这个问题(我想事先说明它是完全类型安全的,并且不包含任何像 typeid 或 dynamic_cast 的黑魔法:)

So, we want to provide only one definition of getTypeInformation() per derived class and it is obvious that it has to be a definition of staticfunction because it is not possible to call "SomeDerivedClass::getTypeInformation()" if getTypeInformation() is virtual. How can we call static function of derived class through pointer to base class? It is not possible with vtable because vtable stores pointers only to virtual functions and since we decided not to use virtual functions, we cannot modify vtable for our benefit. Then, to be able to access static function for derived class through pointer to base class we have to store somehow the type of an object within its base class. One approach is to make base class templatized using "curiously recurring template pattern" but it is not appropriate here and we'll use a technique called "type erasure":

因此,我们只想为每个派生类提供一个 getTypeInformation() 定义,很明显它必须是静态的定义函数,因为如果 getTypeInformation() 是虚拟的,则无法调用“SomeDerivedClass::getTypeInformation()”。我们如何通过指向基类的指针调用派生类的静态函数?vtable 是不可能的,因为 vtable 只存储指向虚函数的指针,而且由于我们决定不使用虚函数,我们不能为了我们的利益修改 vtable。然后,为了能够通过指向基类的指针访问派生类的静态函数,我们必须以某种方式在其基类中存储对象的类型。一种方法是使用“奇怪的重复模板模式”使基类模板化,但在这里并不合适,我们将使用一种称为“类型擦除”的技术:

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

Now we can store the type of an object within base class "Object" with a variable "keeper":

现在我们可以使用变量“keeper”在基类“Object”中存储对象的类型:

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

In a derived class keeper must be initialized during construction:

在派生类中,必须在构造期间初始化:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

Let's add syntactic sugar:

让我们添加语法糖:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

Now declarations of descendants look like:

现在后代的声明看起来像:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

usage:

用法:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

Advantages:

好处:

  1. less duplication of code (but we have to call OVERRIDE_STATIC_FUNCTIONS in every constructor)
  1. 减少重复代码(但我们必须在每个构造函数中调用 OVERRIDE_STATIC_FUNCTIONS)

Disadvantages:

缺点:

  1. OVERRIDE_STATIC_FUNCTIONS in every constructor
  2. memory and performance overhead
  3. increased complexity
  1. 每个构造函数中的 OVERRIDE_STATIC_FUNCTIONS
  2. 内存和性能开销
  3. 增加的复杂性

Open issues:

开放式问题:

1) there are different names for static and virtual functions how to solve ambiguity here?

1)静态和虚函数有不同的名称如何解决这里的歧义?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) how to implicitly call OVERRIDE_STATIC_FUNCTIONS inside every constructor?

2) 如何在每个构造函数中隐式调用 OVERRIDE_STATIC_FUNCTIONS ?

回答by Timo

While Alsk has already given a pretty detailed answer, I'd like to add an alternative, since I think his enhanced implementation is overcomplicated.

虽然 Alsk 已经给出了非常详细的答案,但我想添加一个替代方案,因为我认为他的增强实现过于复杂。

We start with an abstract base class, that provides the interface for all the object types:

我们从一个抽象基类开始,它为所有对象类型提供接口:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

Now we need an actual implementation. But to avoid having to write both the static and the virtual methods, we will have our actual object classes inherit the virtual methods. This does obviously only work, if the base class knows how to access the static member function. So we need to use a template and pass the actual objects class name to it:

现在我们需要一个实际的实现。但是为了避免必须同时编写静态方法和虚拟方法,我们将让我们的实际对象类继承虚拟方法。这显然只有在基类知道如何访问静态成员函数的情况下才有效。所以我们需要使用一个模板并将实际的对象类名传递给它:

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

Finally we need to implement our real object(s). Here we only need to implement the static member function, the virtual member functions will be inherited from the ObjectImpl template class, instantiated with the name of the derived class, so it will access it's static members.

最后,我们需要实现我们的真实对象。这里我们只需要实现静态成员函数,虚成员函数会继承自ObjectImpl模板类,用派生类的名字实例化,所以会访问它的静态成员。

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

Let's add some code to test:

让我们添加一些代码来测试:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

Addendum (Jan 12th 2019):

附录(2019 年 1 月 12 日):

Instead of using the GetClassNameStatic() function, you can also define the the class name as a static member, even "inline", which IIRC works since C++11 (don't get scared by all the modifiers :)):

除了使用 GetClassNameStatic() 函数之外,您还可以将类名定义为静态成员,甚至是“内联”,IIRC 自 C++11 开始工作(不要被所有修饰符吓到 :)):

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

回答by Alexey Malistov

It is possible. Make two functions: static and virtual

有可能的。制作两个函数:静态和虚拟

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

回答by Alexey Malistov

No, this is not possible, because static member functions lack a thispointer. And static members (both functions and variables) are not really class members per-se. They just happen to be invoked by ClassName::member, and adhere to the class access specifiers. Their storage is defined somewhere outside the class; storage is not created each time you instantiated an object of the class. Pointers to class members are special in semantics and syntax. A pointer to a static member is a normal pointer in all regards.

不,这是不可能的,因为静态成员函数缺少this指针。静态成员(函数和变量)本身并不是真正的类成员。它们恰好由 调用ClassName::member,并遵守类访问说明符。它们的存储是在类之外的某个地方定义的;每次实例化类的对象时都不会创建存储。指向类成员的指针在语义和语法上是特殊的。指向静态成员的指针在所有方面都是普通指针。

virtual functions in a class needs the thispointer, and is very coupled to the class, hence they can't be static.

类中的虚函数需要this指针,并且与类非常耦合,因此它们不能是静态的。

回答by tropicana

Well , quite a late answer but it is possible using the curiously recurring template pattern. This wikipediaarticle has the info you need and also the example under static polymorphism is what you are asked for.

嗯,一个很晚的答案,但可以使用奇怪的重复模板模式。这篇维基百科文章有你需要的信息,静态多态下的例子也是你需要的。

回答by Prabhat Kumar

No, Static member function can't be virtual .since virtual concept is resolved at run time with the help of vptr, and vptr is non static member of a class.due to that static member function can't acess vptr so static member can't be virtual.

不,静态成员函数不能是虚拟的。因为虚拟概念是在运行时在 vptr 的帮助下解决的,而 vptr 是类的非静态成员。由于静态成员函数不能访问 vptr,所以静态成员可以不要虚拟。

回答by zumalifeguard

I think what you're trying to do can be done through templates. I'm trying to read between the lines here. What you're trying to do is to call a method from some code, where it calls a derived version but the caller doesn't specify which class. Example:

我认为你要做的事情可以通过模板来完成。我正在尝试阅读这里的字里行间。您要做的是从某些代码中调用一个方法,它调用派生版本但调用者没有指定哪个类。例子:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

You want Try() to call the Bar version of M without specifying Bar. The way you do that for statics is to use a template. So change it like so:

您希望 Try() 在不指定 Bar 的情况下调用 M 的 Bar 版本。您对静态执行此操作的方法是使用模板。所以像这样改变它:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}