I am wondering if there is a way in C++ (especially with C++20) to write some kind of interface for classes/structs.
For example in Java interface is a completely "abstract class" that is used to group related methods with empty bodies:
interface Animal
{
public void animalSound();
public void run();
}
In C++ you could use a pure virtual method declarations to achieve the same behavior.
class Animal
{
public:
virtual void animalSound() = 0;
virtual void run() = 0;
};
But with virtual methods you have runtime costs, and I am not interested in inheritance. So this runtime cost should not be necessary. I just want compile time checks for my "Animal" classes/structs.
With C++20's Concepts I am sure that it is achievable to build a construct that you can apply to a class to guarantee that a certain set of methods is provided.
What I was trying to do looked a bit like this.
template<typename Animal_> concept Animal =
requires()
{
(Animal_{}); // default constructable
(Animal_{}.animalSound());
(Animal_{}.run());
};
But I am not sure that this is very c++ish to do.
(By the way is there a way to require the return type of method to be of a specific type?)
And I am not sure how to attach this to a class/struct.
My first thought was to use a static_assert inside the class/struct:
class Cow
{
private: // compile time type checking
static_assert(std::is_matching_concept<Animal, Cow>);
public:
void animalSound() const noexcept {}
void run() const noexcept {}
};
Where std::is_matching_concept is a placeholder for a constraint that I can not find.
I am looking for best practice feedback and suggestions to solve my problem.
EDIT - Add use case
// given the following code
template<typename Vector_, typename Float_=float> concept Vector =
requires()
{
(Vector_{}); // default constructable
(Vector_{}.X())->Float_;
(Vector_{}.Y())->Float_;
};
[[nodiscard]] constexpr auto Pow2(const auto x) noexcept
{
return x * x;
}
[[nodiscard]] constexpr auto LengthPow2(Vector auto vec) noexcept // the use of Vector
{
return Pow2(vec.X()) + Pow2(vec.Y());
}
// Now I want to implement a Vector
// and I want compile time checking, that I have no missed any properties
struct VectorImpl1
{
// EDITED: as @QuentinUK mentioned the static_assert should be in a public scope
// "If in the private part of a class the concepts
// can pass for private members which isn't what you'd want."
public:
// EDITED: as @DavisHerring mentioned this is the way to go
static_assert(Vector<VectorImpl1>);
public:
constexpr VectorImpl1() noexcept = default;
constexpr VectorImpl1(float x, float y) noexcept : x_(x), y_(y) {}
private:
float x_{};
float y_{};
public:
[[nodiscard]] constexpr float X() const noexcept
{ return x_; }
[[nodiscard]] constexpr float Y() const noexcept
{ return y_; }
};
struct VectorImpl2
{
public:
static_assert(Vector<VectorImpl2>);
public:
constexpr VectorImpl2() noexcept = default;
constexpr VectorImpl2(float rad, float length) noexcept : rad_(rad), length_(length) {}
private:
float rad_{};
float length_{};
public:
[[nodiscard]] constexpr float X() const noexcept
{ return CalcX(rad_, length_); }
[[nodiscard]] constexpr float Y() const noexcept
{ return CalcY(rad_, length_); }
};