【问题标题】:Use C++20 Concepts to force a class to implement a set of methods使用 C++20 概念强制类实现一组方法
【发布时间】:2020-09-03 15:30:37
【问题描述】:

我想知道 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_); }
};

【问题讨论】:

  • 这不就是 CRTP 的用途吗?
  • "所以这个运行时成本应该是不必要的。我只想对我的“动物”类/结构进行编译时检查。“我不明白你的用例在这里.你会使用某种多态性吗?也就是说,您是否要将该类型传递给不知道确切它是什么类型的东西?如果不是,那么就没有什么可以验证的了。如果是这样,那么你应该有一些接口可以接收和使用实现所述接口的对象。仅验证声明与概念匹配是没有意义的。
  • std::is_matching_concept&lt;C,T&gt; 拼写为C&lt;T&gt;,尽管您必须将static_assert 放在类之后。
  • static_assert 可以在类中。 (但如果在类的私有部分,概念可以传递给私有成员,这不是您想要的。)

标签: c++ class templates c++20 c++-concepts


【解决方案1】:

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

如果由于某种原因你想在同一个头文件中得到一个编译错误,你可以这样做:

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)),

      |               ~~~~~~~~~~~~~~~~~~^~~

godbolt

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

这可以实现,在概念之前,使用detection idiom,我建议看这个:two part talk by Walter Brown或阅读this blog by Simon Brand

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-04
    • 2021-11-27
    • 1970-01-01
    • 2021-07-12
    • 2020-11-12
    相关资源
    最近更新 更多