C++ 无运行时开销的可选类成员

C++ 无运行时开销的可选类成员,c++,C++,我有一个非常普遍的问题,我还没有找到令人满意的解决方案: 所以我想要有两个基本相同的类A和AData,除了后者有一个额外的属性数据,并且每个类都支持一个函数foo,这是不同的,因为它取决于额外数据的存在 愚蠢的解决方案是复制整个类并稍微修改它,但这会导致代码重复,并且很难维护。使用std::optional或指针会导致额外的检查,从而导致运行时开销,对吗 我的问题是,是否有一种方法可以获得与只复制代码而不复制实际代码相同的运行时性能?我目前的解决方案是将AData作为派生类,并将其声明为a的友元

我有一个非常普遍的问题,我还没有找到令人满意的解决方案: 所以我想要有两个基本相同的类A和AData,除了后者有一个额外的属性数据,并且每个类都支持一个函数foo,这是不同的,因为它取决于额外数据的存在

愚蠢的解决方案是复制整个类并稍微修改它,但这会导致代码重复,并且很难维护。使用std::optional或指针会导致额外的检查,从而导致运行时开销,对吗


我的问题是,是否有一种方法可以获得与只复制代码而不复制实际代码相同的运行时性能?我目前的解决方案是将AData作为派生类,并将其声明为a的友元,然后重写虚拟函数foo,但由于使用友元,我不喜欢这种方法。

您可以使用静态多态性和奇怪的循环模板模式。
A和AData都提供了foo,但行为通过doFoo是类特定的。另外,不使用虚拟分派可以避免vtable查找的运行时开销

template <typename TData>
class Abase
{
public:
   void foo()
   {
       static_cast<TData*>(this)->doFoo();
   }
};

class A : public Abase<A>
{
    friend ABase<A>;
    void doFoo() { cout << "A::foo()\n"; }
};

class AData : public Abase<AData>
{
   friend Abase<AData>;
   int someDataMember;
   void doFoo() {  cout << "AData::foo()\n"; /*... use someDataMember ... */}
};

您可以使用静态多态性和奇怪的循环模板模式。
A和AData都提供了foo,但行为通过doFoo是类特定的。另外,不使用虚拟分派可以避免vtable查找的运行时开销

template <typename TData>
class Abase
{
public:
   void foo()
   {
       static_cast<TData*>(this)->doFoo();
   }
};

class A : public Abase<A>
{
    friend ABase<A>;
    void doFoo() { cout << "A::foo()\n"; }
};

class AData : public Abase<AData>
{
   friend Abase<AData>;
   int someDataMember;
   void doFoo() {  cout << "AData::foo()\n"; /*... use someDataMember ... */}
};

为什么不使用组合:

class A
{
public:
    void foo() { /*...*/ }
};

class AData
{
   A a;
   int someDataMember;
public:
    void foo() { /*... use someDataMember ...*/ }
};

为什么不使用组合:

class A
{
public:
    void foo() { /*...*/ }
};

class AData
{
   A a;
   int someDataMember;
public:
    void foo() { /*... use someDataMember ...*/ }
};

你为什么要把它声明为朋友?继承的目的是解决你提到的问题,还是我遗漏了什么?也许你可以把它放在一个小的代码示例中,我们或者只有我能更好地理解它。因为函数foo在这两种情况下都需要访问私有成员。我可能应该澄清一下。你不能让私有成员受到保护吗?顺便说一句:使用std::optional或std::variant可能与通过vtable调用具有相同的开销。它几乎不依赖于编译器的优化。对我来说,真正的用例是什么还不清楚!使用派生类和虚拟方法很好,不需要朋友。为什么需要将其声明为朋友?继承的目的是解决你提到的问题,还是我遗漏了什么?也许你可以把它放在一个小的代码示例中,我们或者只有我能更好地理解它。因为函数foo在这两种情况下都需要访问私有成员。我可能应该澄清一下。你不能让私有成员受到保护吗?顺便说一句:使用std::optional或std::variant可能与通过vtable调用具有相同的开销。它几乎不依赖于编译器的优化。对我来说,真正的用例是什么还不清楚!使用派生类和虚拟方法很好,不需要朋友。另外,不使用虚拟分派也可以提高性能。但必须在某处进行调度。如果它使用std::variant,则也需要时间。也可使用if/else进行手动调度。通常vtable分派非常快,std::variant可以相同,但不能更快。@Klaus:这里的分派是在编译时完成的,因此没有运行时开销。vtable查找是在运行时执行的。我同意克劳斯的观点。要么我们知道具体类型,不需要虚拟分派,要么我们不知道,我们只是将分派移到类之外进行管理。您在这里没有分派。如果您存储这两种类型的元素并希望调用obejct的函数,则会出现这种需求。此时必须进行分派。这里的分派是编译器将在模板实例化时调用正确的调用。也可以内联调用。在任何情况下,都不会通过vtable进行运行时调度。是的,如果我们真的想在运行时将这两次多态性都视为通过基类指针访问派生类,那么就需要虚拟行为,因为不使用虚拟分派也可以提高性能。但必须在某处进行调度。如果它使用std::variant,则也需要时间。也可使用if/else进行手动调度。通常vtable分派非常快,std::variant可以相同,但不能更快。@Klaus:这里的分派是在编译时完成的,因此没有运行时开销。vtable查找是在运行时执行的。我同意克劳斯的观点。要么我们知道具体类型,不需要虚拟分派,要么我们不知道,我们只是将分派移到类之外进行管理。您在这里没有分派。如果您存储这两种类型的元素并希望调用obejct的函数,则会出现这种需求。此时必须进行分派。这里的分派是编译器将在模板实例化时调用正确的调用。也可以内联调用。在任何情况下,都不会通过vtable进行运行时调度。是的,若你们真的想在运行时处理这两次多态,就像通过基类指针访问派生类一样,那个么虚拟b
ehaviour将被需要+1:如果OP没有关于conrete使用场景的任何进一步细节,那么合成应该是更好的设计方法,除此之外,可能还有一个私有继承。我认为它的行为与复制的类完全不同,因为使用foo的类中的函数不会被替换,或者你能以某种方式使其工作吗?也许我应该更准确地说明需求是什么。+1:OP没有关于conrete使用场景的任何进一步细节,合成应该是更好的设计方法,除此之外,可能还有一个私有继承。我认为它的行为与复制的类完全不同,因为使用foo的类中的函数不会被替换,或者你能以某种方式使其工作吗?也许我应该更准确地说明要求是什么。