如何使用概念在 C++ 20 中提供默认实现?
How to provide default implementations in C++ 20 using concepts?
我正在试验将 C++20 中引入的 concept
用作静态接口的可能性。到目前为止,我做得很好,只是我无法找到一种方法来为概念提供“默认实现”。
例如,我有一个名为ByteBuffer
的概念,它是这样表述的:
template <typename T>
concept ByteBuffer = requires (T t) {
{ t.read_byte() } noexcept -> std::convertible_to<uint8_t>;
{ t.has_byte() } noexcept -> std::same_as<bool>;
/* default implementation for `t.read_until(...)`? */
};
从逻辑上讲,我应该能够为 t.read_until(...)
(省略参数)提供默认实现,并允许具体实现覆盖默认实现。有办法吗?我该怎么做?
如果这不行的话,我觉得加上这样的能力也是合理的。
目前,我不得不求助于 CRTP 来提供外观 class,我认为它比我想象的还要多余。
概念是不是基础类。事实上,概念与其任何模板参数都没有显式或隐式关系。
概念只做一件事:验证一组特定的模板参数是否适合在实例化特定模板时使用。那是 全部.
概念包含一系列表达式和术语,这些表达式和术语应该对给定的模板参数有效。这就是确定一组模板参数是否对特定用途有效所需的全部内容,因此这就是所有概念提供的内容。
如果您需要某种默认功能,则必须使用替代的 C++ 机制来实现。最简单的是一个有自己约束的效用函数:
template<typename T>
concept ByteBufferReadUntil = ByteBuffer<T> &&
requires(T bb) //Add parameters as appropriate
{
{ t.read_until() } noexcept -> std::same_as<bool>;
};
template<ByteBuffer T>
requires ByteBufferReadUntil<T>
bool read_byte_buffer_until(T &bb)
{
return t.read_until();
}
template<ByteBuffer T>
bool read_byte_buffer_until(T &bb)
{
//default implementation for `t.read_until(...)`
}
所以每当你想为任意 ByteBuffer
做 read_until
时,你调用 read_byte_buffer_until
.
概念实际上只是对类型的约束,因此向概念添加“默认实现”是没有意义的。相反,您可以做的是为 read_until
提供默认实现并使用它,除非某些 ByteBuffer
类型提供了自己的实现。
首先,您可以编写一个单独的概念来检查 read_until
:
template <typename T>
concept Readable = requires (T t) {
{ t.read_until() } noexcept -> std::same_as<void>; // takes no arguments, and returns void for demonstration
};
然后您可以为不提供此功能的类型提供默认实现:
template <typename T>
void read_until_impl(T t) {
std::cout << "default implementation";
}
template <Readable T>
void read_until_impl(T t) {
t.read_until(); // call provided member function
}
然后一些使用 ByteBuffer
类型的函数可以这样调用函数:
template <ByteBuffer T>
void use(T t) {
read_until_impl(t);
}
这里是 demo。
如前所述,C++20 概念实际上根本没有提供任何机制来帮助解决这个问题。它们只是 约束模板的谓词。 没什么 更多。
我将提供一个更简洁的替代方案:使用 if constexpr
:
template <ByteBuffer BB>
void read_until(BB& buffer) {
if constexpr (requires { buffer.read_until(); }) {
buffer.read_until();
} else {
while (buffer.has_byte()) {
buffer.read_byte();
}
}
}
或者有时我喜欢使用函数组合来使这一点更清楚,使用类似 Boost.Hof:
auto read_until = first_of(
[](ByteBuffer auto& bb) requires requires { bb.read_until(); } {
// specialized impl
},
[](ByteBuffer auto& bb) {
// default impl
});
这也可以是 overload
,因为一个比另一个更受限制,我只是喜欢使用 first_of
因为它使它更清晰(而且 plus 可以在 C++ 中工作17 只有一个基于尾随 return 类型的约束)。
概念不基础类。事实上,概念与其任何模板参数都没有显式或隐式关系。
概念只做一件事:验证一组特定的模板参数是否适合在实例化特定模板时使用。就这些了。
概念包含一系列表达式和术语,这些表达式和术语应该对给定的模板参数有效。这就是确定一组模板参数是否对特定用途有效所需的全部内容,因此这就是所有概念提供的内容。
如果您需要某种默认功能,则必须使用替代的 C++ 机制来实现。最简单的是一个有自己约束的效用函数:
template<typename T>
concept ByteBufferReadUntil = ByteBuffer<T> &&
requires(T bb) //Add parameters as appropriate
{
{ t.read_until() } noexcept -> std::same_as<bool>;
};
template<ByteBuffer T>
requires ByteBufferReadUntil<T>
bool read_byte_buffer_until(T &bb)
{
return t.read_until();
}
template<ByteBuffer T>
bool read_byte_buffer_until(T &bb)
{
//default implementation for `t.read_until(...)`
}
所以每当你想为任意 ByteBuffer 做 read_until 时,你调用 read_byte_buffer_until.
我正在试验将 C++20 中引入的 concept
用作静态接口的可能性。到目前为止,我做得很好,只是我无法找到一种方法来为概念提供“默认实现”。
例如,我有一个名为ByteBuffer
的概念,它是这样表述的:
template <typename T>
concept ByteBuffer = requires (T t) {
{ t.read_byte() } noexcept -> std::convertible_to<uint8_t>;
{ t.has_byte() } noexcept -> std::same_as<bool>;
/* default implementation for `t.read_until(...)`? */
};
从逻辑上讲,我应该能够为 t.read_until(...)
(省略参数)提供默认实现,并允许具体实现覆盖默认实现。有办法吗?我该怎么做?
如果这不行的话,我觉得加上这样的能力也是合理的。
目前,我不得不求助于 CRTP 来提供外观 class,我认为它比我想象的还要多余。
概念是不是基础类。事实上,概念与其任何模板参数都没有显式或隐式关系。
概念只做一件事:验证一组特定的模板参数是否适合在实例化特定模板时使用。那是 全部.
概念包含一系列表达式和术语,这些表达式和术语应该对给定的模板参数有效。这就是确定一组模板参数是否对特定用途有效所需的全部内容,因此这就是所有概念提供的内容。
如果您需要某种默认功能,则必须使用替代的 C++ 机制来实现。最简单的是一个有自己约束的效用函数:
template<typename T>
concept ByteBufferReadUntil = ByteBuffer<T> &&
requires(T bb) //Add parameters as appropriate
{
{ t.read_until() } noexcept -> std::same_as<bool>;
};
template<ByteBuffer T>
requires ByteBufferReadUntil<T>
bool read_byte_buffer_until(T &bb)
{
return t.read_until();
}
template<ByteBuffer T>
bool read_byte_buffer_until(T &bb)
{
//default implementation for `t.read_until(...)`
}
所以每当你想为任意 ByteBuffer
做 read_until
时,你调用 read_byte_buffer_until
.
概念实际上只是对类型的约束,因此向概念添加“默认实现”是没有意义的。相反,您可以做的是为 read_until
提供默认实现并使用它,除非某些 ByteBuffer
类型提供了自己的实现。
首先,您可以编写一个单独的概念来检查 read_until
:
template <typename T>
concept Readable = requires (T t) {
{ t.read_until() } noexcept -> std::same_as<void>; // takes no arguments, and returns void for demonstration
};
然后您可以为不提供此功能的类型提供默认实现:
template <typename T>
void read_until_impl(T t) {
std::cout << "default implementation";
}
template <Readable T>
void read_until_impl(T t) {
t.read_until(); // call provided member function
}
然后一些使用 ByteBuffer
类型的函数可以这样调用函数:
template <ByteBuffer T>
void use(T t) {
read_until_impl(t);
}
这里是 demo。
如前所述,C++20 概念实际上根本没有提供任何机制来帮助解决这个问题。它们只是 约束模板的谓词。 没什么 更多。
我将提供一个更简洁的替代方案:使用 if constexpr
:
template <ByteBuffer BB>
void read_until(BB& buffer) {
if constexpr (requires { buffer.read_until(); }) {
buffer.read_until();
} else {
while (buffer.has_byte()) {
buffer.read_byte();
}
}
}
或者有时我喜欢使用函数组合来使这一点更清楚,使用类似 Boost.Hof:
auto read_until = first_of(
[](ByteBuffer auto& bb) requires requires { bb.read_until(); } {
// specialized impl
},
[](ByteBuffer auto& bb) {
// default impl
});
这也可以是 overload
,因为一个比另一个更受限制,我只是喜欢使用 first_of
因为它使它更清晰(而且 plus 可以在 C++ 中工作17 只有一个基于尾随 return 类型的约束)。
概念不基础类。事实上,概念与其任何模板参数都没有显式或隐式关系。
概念只做一件事:验证一组特定的模板参数是否适合在实例化特定模板时使用。就这些了。
概念包含一系列表达式和术语,这些表达式和术语应该对给定的模板参数有效。这就是确定一组模板参数是否对特定用途有效所需的全部内容,因此这就是所有概念提供的内容。
如果您需要某种默认功能,则必须使用替代的 C++ 机制来实现。最简单的是一个有自己约束的效用函数:
template<typename T>
concept ByteBufferReadUntil = ByteBuffer<T> &&
requires(T bb) //Add parameters as appropriate
{
{ t.read_until() } noexcept -> std::same_as<bool>;
};
template<ByteBuffer T>
requires ByteBufferReadUntil<T>
bool read_byte_buffer_until(T &bb)
{
return t.read_until();
}
template<ByteBuffer T>
bool read_byte_buffer_until(T &bb)
{
//default implementation for `t.read_until(...)`
}
所以每当你想为任意 ByteBuffer 做 read_until 时,你调用 read_byte_buffer_until.