使用 C++20 概念强制类实现一组方法



我想知道C++(尤其是 C++20(是否有办法为类/结构编写某种接口。

例如,在Java接口中是一个完全"抽象的类",用于将相关方法与空体分组:

interface Animal
{
public void animalSound();
public void run();
}

在C++您可以使用纯虚拟方法声明来实现相同的行为。

class Animal
{
public:
virtual void animalSound() = 0;
virtual void run() = 0;
};

但是对于虚拟方法,您需要运行成本,我对继承不感兴趣。 因此,此运行时成本应该是不必要的。我只想对我的"动物"类/结构进行编译时检查。

有了C++20的概念,我相信建立一个结构是可以实现的 您可以应用于类以保证提供一组特定的方法。

我想做的事情看起来有点像这样。

template<typename Animal_> concept Animal =
requires()
{
(Animal_{}); // default constructable
(Animal_{}.animalSound());
(Animal_{}.run());
};

但我不确定这是否非常 c++。

(顺便问一下,有没有办法要求方法的返回类型是特定类型?

而且我不确定如何将其附加到类/结构。

我的第一个想法是在类/结构中使用static_assert

class Cow
{
private: // compile time type checking
static_assert(std::is_matching_concept<Animal, Cow>);
public:
void animalSound() const noexcept {}
void run() const noexcept {}
};

其中std::is_matching_concept是我找不到的约束的占位符。

我正在寻找最佳实践反馈和建议来解决我的问题。

编辑 - 添加用例

// 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_); }
};

你可以,问题是你为什么要这样做。 如果你的类型没有按照它应该做的事情 - 你会得到一个编译错误,对吧?

如果由于某种原因想要在同一标头中出现编译错误,可以执行以下操作:

template <typename ...>
using void_t = void;    // available since c++17 in std
template <typename T>
using cow_test = void_t<
decltype(std::declval<T>().moo(0)),
decltype(std::declval<T>().chew(0))
>;
class cow {
public:
void moo(int);
void chew(int);
};
using test_cow = cow_test<cow>;
class cat {
public:
void meaw(int);
void chew(int);
};
using test_cat = cow_test<cat>;

这将在以下情况下test_cat失败:

r #1) C++
x86-64 gcc 10.1
Compiler options...
1
<Compilation failed>
x86-64 gcc 10.1 - 364ms
#1 with x86-64 gcc 10.1
<source>: In substitution of 'template<class T> using cow_test = void_t<decltype (declval<T>().moo(0)), decltype (declval<T>().chew(0))> [with T = cat]':
<source>:26:30:   required from here
<source>:8:33: error: 'class cat' has no member named 'moo'
8 |      decltype(std::declval<T>().moo(0)),
|               ~~~~~~~~~~~~~~~~~~^~~

戈德博尔特

不过,我怀疑你想要的是根据这些信息采取行动:基本上 - 如果我的班级可以moo()- moo,否则meaw.

这可以在概念之前通过使用detection idiom来实现,我建议看这个:沃尔特布朗的两部分谈话或阅读西蒙布兰德的博客

相关内容

最新更新