[英]Use C++20 Concepts to force a class to implement a set of methods
I am wondering if there is a way in C++ (especially with C++20) to write some kind of interface for classes/structs.我想知道 C++(尤其是 C++20)中是否有办法为类/结构编写某种接口。
For example in Java interface is a completely "abstract class" that is used to group related methods with empty bodies:例如在 Java 中,接口是一个完全“抽象类”,用于将相关方法与空主体进行分组:
interface Animal
{
public void animalSound();
public void run();
}
In C++ you could use a pure virtual method declarations to achieve the same behavior.在 C++ 中,您可以使用纯虚方法声明来实现相同的行为。
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.但是使用虚拟方法会产生运行时成本,我对 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.使用 C++20 的概念,我确信构建一个可以应用于 class 的构造是可以实现的,以保证提供一组特定的方法。
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.但我不确定这是不是非常c++ish。
(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:我的第一个想法是在类/结构中使用
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 {}
};
Where std::is_matching_concept
is a placeholder for a constraint that I can not find.其中
std::is_matching_concept
是我找不到的约束的占位符。
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_); }
};
You can, the question is why you want to do this.你可以,问题是你为什么要这样做。 If your type doesn't do what it's suppose to - you'll get a compilation error, right?
如果你的类型没有做它应该做的事情——你会得到一个编译错误,对吧?
If you want to get a compilation error in the same header for some reason, you can do something like:如果由于某种原因您想在同一个 header 中出现编译错误,您可以执行以下操作:
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>;
This will fail in test_cat
with:这将在
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)),
| ~~~~~~~~~~~~~~~~~~^~~
I have a suspicion, though, that what you want is to act upon this information: basically - if my class can moo()
- moo, otherwise meaw
.不过,我怀疑你想要的是根据这些信息采取行动:基本上 - 如果我的 class 可以
moo()
- moo,否则meaw
。
This can be achieved, before concepts, by using detection idiom
, I suggest watching this: two part talk by Walter Brown or read this blog by Simon Brand这可以在概念之前通过使用
detection idiom
来实现,我建议看这个: Walter Brown 的两部分谈话或阅读Simon Brand 的这个博客
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.