-1

I have a code like this using CRTP and C++20:

template<class Derived>
class Base {
public:

    void m() {
        static_cast<Derived*>(this)->feature();      
    }
   
    virtual constexpr void feature() = 0;
}

class BaseImpl: public Base<BaseImpl> {
     virtual constexpr void feature() final override  { // ... };
}

Is there a way to remove vptr so the object won't take 8 bytes (for x64), and instead will take only 1 byte? (since it never uses runtime polymorphism)?

In real code, the hierarchy is much more complex and it has 2 vptr (so it takes 16 bytes). Are there any extensions like for GCC or MSVC?

Yeah, definitely, one of the solutions is just to remove the virtual method. But since it has a complex hierarchy, it brings extremely ugly errors and unmaintainable code, since programmers should guess what methods must be implemented via reading unreadable template instantiation errors.

The whole goal is to achieve a Java-like hierarchy (with interfaces, override checks, etc), but with zero-cost abstraction.

I did some experiments (exploring code disassembly) and the compiler completely optimized all the virtual calls, providing zero-cost abstraction. EXCEPT it extends the object to have a vptr which is never used in my code.

I found something like __declspec(novtable) but vptr still takes space. And yes, the size of the objects is extremely important to be as small as possible.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 14
    Don't use `virtual`. – Eljay Aug 22 '22 at 20:14
  • To enforce interface, you might have contraints/concept in C++20, instead of `virtual` . – Jarod42 Aug 22 '22 at 20:18
  • If you can wait for C++23, you can replace the CRTP with the new ["deducing this"](https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/) feature, eg: `class Base { public: template void m(this Self&& self) { self.feature(); } }; class BaseImpl : public Base { constexpr void feature() { ... }; };` – Remy Lebeau Aug 23 '22 at 01:13

1 Answers1

8

You're using CRTP, which uses static dispatch. There's absolutely no reason to use virtual here. If you want to ensure that the method exists with the right signature, use a static_assert.

template<class Derived>
class Base {
public:

    void m() {
        static_cast<Derived*>(this)->feature();      
    }
    
    ~Base() {
        static_assert(std::is_same_v<void,decltype(static_cast<Derived*>(this)->feature())>);
    }
};

class BaseImpl: public Base<BaseImpl> {
public:
     constexpr void feature()  {  };
};
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Yeah, I was thinking about using static_assert or concepts (as for me, maybe it compiler bug, but the concept was always violated when using for Derived). But in general, code looks ugly with all this decltype, is_same_v, static_assert, static_cast... it's hard to read, for all these check errors you need to explore the base class, or write a custom error message for each, which add even more tautology thing. If only there were no that 8 bytes for vptr, it would the perfect solution for me. – vitalii-32 Aug 22 '22 at 23:00
  • just compare: `static_assert(std::is_same_v(this)->feature())>);` vs `virtual void feature() = 0` – vitalii-32 Aug 22 '22 at 23:00
  • @vitalii-32 The ugly `static_cast` is a completely unrelated problem, and trivially solvable via `Derived& self() {return *static_cast(this);}` https://coliru.stacked-crooked.com/a/007f3a637cef9f69 – Mooing Duck Aug 26 '22 at 15:36
  • And you don't need the static_assert or decltype or anything. That's merely there as a safety check https://coliru.stacked-crooked.com/a/81595fb3ca511ea8 – Mooing Duck Aug 26 '22 at 15:42