brevzin / cpp_proposals

My WG21 proposals
35 stars 22 forks source link

Add selead classes #41

Closed klappdev closed 1 year ago

klappdev commented 1 year ago

I'm very impressed with the huge contribution you make to C++. I want to offer you an idea for improving C++.

Often during developing, it is necessary to create a hierarchy with one base class and a finite number of descendants.

class figure {
public:
    virtual ~figure() = default;

    virtual int square() = 0;
};

struct rectangle final : public figure {
    int square() override {
        return 2 * width + 2 * height;
    }

    int width, height;
};

struct circle final : public figure {
    int square() override {
        return 2 * M_PI * radius;
    }

    int radius;
};

The function to get the area of any of the figures can be described as follows

int square(const figure& f) {
    if (typeid(f) == typeid(rectangle)) {
        return static_cast<rectangle&>(f).square();
    } else if (typeid(f) == typeid(circle)) {
        return static_cast<rectangle&>(f).square();
    } else {
        assert(false);
    }
}

Since virtual functions, RTTI are used, this leads to code performance degradation. As an output, it could use std::variant and std::visit. The disadvantage of std::variant is that polymorphism must be abandoned. https://en.cppreference.com/w/cpp/utility/variant

If the class hierarchy cannot be abandoned, CRTP can be used. But this approach also has disadvantages:

Propose to add the sealed keyword, which can be used to mark the base (abstract) class. It indicates that the given base (abstract) class has a finite number of descendants. And instead of a virtual table under the hood, compiler could use a static array of descendants.

class figure sealed {   // similar - final
public:
    virtual ~figure() = default;

    virtual int square() = 0;
};

Since the compiler knows a finite number of descendants, it can optimize the code more strongly. For example, do not use RTTI at all. Or make typeid work at compile time.

int square(const figure& f) {
    if (typeid(f) == typeid(rectangle)) {
        return static_cast<rectangle&>(f).square();
    } else if (typeid(f) == typeid(bad_figure)) {       // compile time error: no branch with `circle`
        return static_cast<rectangle&>(f).square();
    }
}

Also it don't need to write an else branch. If you skip at least one of the descendants in the if-else-if chain, then the compiler at compile time could tell, which successor was skipped.

Similar classes have been added to Java, Kotlin, Scala. https://docs.oracle.com/en/java/javase/15/language/sealed-classes-and-interfaces.html https://kotlinlang.org/docs/sealed-classes.html

sealed classes are similar to enum types in Rust, Swift. https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html

This is similar to std::variant, but only at the language level. I very want to see such a feature in C++.

brevzin commented 1 year ago

This isn't really the place to propose language features. You could email the std-proposals list.