C++20:从0到1学懂concept

这篇具有很好参考价值的文章主要介绍了C++20:从0到1学懂concept。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

  • 1.concept语法

    • 1.1 替换typename

    • 1.2 requires关键字

    • 1.4 concept与auto

  • 2.编译器支持

  • 3.总结

C++20引入了concept(概念),是对模板参数(编译时评估)的一组约束。你可以将它们用于类模板和函数模板来控制函数重载和特化。一些优点包括:

  • 对模版参数强制类型约束

  • 提高代码可读性(替换了较长的SFINAE代码)

  • 提供更友好的报错信息

  • 通过限制可以使用的类型来防止意外的模板实例化

往期C++20的系列文章:

1.C++那些事之C++20协程开篇

2.盘点C++20模块那些事


注:本篇所有代码已更新于星球。


下面进入正文,以一个比较简单加法为例。

#include <iostream>

struct Foo {};

template <typename T>
T Add(T a, T b) {
  return a + b;
}

int main() {
  std::cout << Add(1, 2) << std::endl;
  Foo f1, f2;
  std::cout << Add(f1, f2) << std::endl;
  return 0;
}

对于Foo来说,是不支持加法的,于此同时也是不可以直接std::cout << ,因此在编译时报一大堆错误,包含operator<<operator+,但这并不是我们期望的错误信息,我们比较期望的是编译器给我们最直观的错误信息,即:这个结构体能不能相加。

add.cc: In function 'int main()':
add.cc:13:13: error: no match for 'operator<<' (operand types are 'std::ostream' {aka 'std::basic_ostream<char>'} and 'Foo')
   13 |   std::cout << Add(f1, f2) << std::endl;
      |   ~~~~~~~~~ ^~ ~~~~~~~~~~~
      |        |          |
      |        |          Foo
      |        std::ostream {aka std::basic_ostream<char>}
In file included from /usr/local/Cellar/gcc/13.2.0/include/c++/13/iostream:41,
                 from add.cc:1:
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream:110:7: note: candidate: 'std::basic_ostream<_CharT, _Traits>::__ostream_type& std::basic_ostream<_CharT, _Traits>::operator<<(__ostream_type& (*)(__ostream_type&)) [with _CharT = char; _Traits = std::char_traits<char>; __ostream_type = std::basic_ostream<char>]'
  110 |       operator<<(__ostream_type& (*__pf)(__ostream_type&))
  
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream:801:5: note:   template argument deduction/substitution failed:
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream: In substitution of 'template<class _Ostream, class _Tp> _Ostream&& std::operator<<(_Ostream&&, const _Tp&) [with _Ostream = std::basic_ostream<char>&; _Tp = Foo]':
add.cc:13:26:   required from here
/usr/local/Cellar/gcc/13.2.0/include/c++/13/ostream:801:5: error: no type named 'type' in 'struct std::enable_if<false, void>'
add.cc: In instantiation of 'T Add(T, T) [with T = Foo]':
add.cc:13:19:   required from here
add.cc:7:12: error: no match for 'operator+' (operand types are 'Foo' and 'Foo')
    7 |   return a + b;
      |          ~~^~~

当我们使用concept实现之后:

template <typename T>
concept Addable = requires(T a, T b) { a + b; };

template <typename T>
  requires Addable<T>
T Add(T a, T b) {
  return a + b;
}

便可以得到我们关心的编译错误:

add_concept.cc: In function 'int main()':
add_concept.cc:17:19: error: no matching function for call to 'Add(Foo&, Foo&)'
   17 |   std::cout << Add(f1, f2) << std::endl;
      |                ~~~^~~~~~~~
add_concept.cc:10:3: note: candidate: 'template<class T>  requires  Addable<T> T Add(T, T)'
   10 | T Add(T a, T b) {
      |   ^~~
add_concept.cc:10:3: note:   template argument deduction/substitution failed:
add_concept.cc:10:3: note: constraints not satisfied
add_concept.cc: In substitution of 'template<class T>  requires  Addable<T> T Add(T, T) [with T = Foo]':
add_concept.cc:17:19:   required from here
add_concept.cc:6:9:   required for the satisfaction of 'Addable<T>' [with T = Foo]
add_concept.cc:6:19:   in requirements with 'T a', 'T b' [with T = Foo]
add_concept.cc:6:42: note: the required expression '(a + b)' is invalid
    6 | concept Addable = requires(T a, T b) { a + b; };
      |                                        ~~^~~

下面,我们来针对上面这个例子深入学习concept语法。

1.concept语法

语法:

template <template-parameter-list>
concept concept-name = constraint-expression;

我们来对比一下实际的例子,Addable是concept-name,constraint-expression是requires(T a, T b) { a + b; }

template <typename T>
concept Addable = requires(T a, T b) { a + b; };

使用方式为:

#include <concepts>

这个concept可以放在多个地方,如下:

  • typename的位置

  • requires后面

  • auto前面

1.1 替换typename

约束模版参数,替换typename。

// template <typename T>  typename->Addable
template <Addable T>
T Add(T a, T b) {
  return a + b;
}

1.2 requires关键字

我们在函数模板中使用 requires 关键字。它可以访问我们的模板T是否是可以相加的,如果模板可以处理相加,它将返回 true。

requires可以放在模版中,也可以放在函数之后,但是不可以放在类之后。于此同时它有两个写法:

  • requires 条件

例如:

template <typename T>
  requires Addable<T>
T Add(T a, T b) { ... }
  • requires 表达式

例如:

template <typename T>
concept Addable = requires(T a, T b) { a + b; };
1.2.1 放在模版中

函数:

template <typename T>
  requires Addable<T>
T Add(T a, T b) {
  return a + b;
}

类:

template <typename T>
 requires Addable<T> 
class Bar {
 public:
  T Add(T a, T b) { return a + b; }
};
1.2.2 函数尾随 Requires 子句

函数:

template <typename T>
T Add(T a, T b)
  requires Addable<T>
{
  return a + b;
}

对于类则不支持这种写法,会报错:error: expected unqualified-id before 'requires'  28 |   requires Addable

template <typename T>
class Bar requires Addable<T> 
{
 public:
  T Add(T a, T b) { return a + b; }
};
1.2.3 requires基本示例

以数据库当中的类型为例,数据库中有不同类型,我们将其划分为:null、binary、number等,我们想要对传递的类型执行打印操作,于是有了下面的示例:

#include <concepts>

class NumberType {};
class BaseBinaryType {};
class NullType {};
class FloatingPointType : public NumberType {};
class IntegerType : public NumberType {};
class BinaryType: public BaseBinaryType {};

template <typename T>
  requires std::is_base_of_v<NumberType, T> || std::is_base_of_v<BaseBinaryType, T>
void PrintValue(T v) {}

int main() {
  PrintValue(FloatingPointType{});
  PrintValue(NullType{});
  return 0;
}

对于requires我们可以使用||,上面示例中出现了NullType,它不满足requires,因此会编译出现:

concept_requires.cc:16:13: error: no matching function for call to 'PrintValue(NullType)'
   16 |   PrintValue(NullType{});
      |   ~~~~~~~~~~^~~~~~~~~~~~
concept_requires.cc:12:6: note: candidate: 'template<class T>  requires (is_base_of_v<NumberType, T>) || (is_base_of_v<BaseBinaryType, T>) void PrintValue(T)'
   12 | void PrintValue(T v) {}
      |      ^~~~~~~~~~
concept_requires.cc:12:6: note:   template argument deduction/substitution failed:
concept_requires.cc:12:6: note: constraints not satisfied
concept_requires.cc: In substitution of 'template<class T>  requires (is_base_of_v<NumberType, T>) || (is_base_of_v<BaseBinaryType, T>) void PrintValue(T) [with T = NullType]':
concept_requires.cc:16:13:   required from here
concept_requires.cc:12:6:   required by the constraints of 'template<class T>  requires (is_base_of_v<NumberType, T>) || (is_base_of_v<BaseBinaryType, T>) void PrintValue(T)'
concept_requires.cc:11:45: note: no operand of the disjunction is satisfied
   11 |   requires std::is_base_of_v<NumberType, T> || std::is_base_of_v<BaseBinaryType, T>
      |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

1.4 concept与auto

当auto出现时,我们可以将其与concept一起使用,例如:

auto add(auto x, auto y) {
  return x + y;
}

我们可以变为:

template <typename T>
concept Addable = requires(T a, T b) { a + b; };

auto add2(Addable auto x, Addable auto y) {
  return x + y;
}

编译时会出现:

concept_auto.cc:17:20: error: no matching function for call to 'add2(Foo&, Foo&)'
   17 |   std::cout << add2(f1, f2) << std::endl;
      |                ~~~~^~~~~~~~
concept_auto.cc:10:6: note: candidate: 'template<class auto:18, class auto:19>  requires (Addable<auto:18>) && (Addable<auto:19>) auto add2(auto:18, auto:19)'
   10 | auto add2(Addable auto x, Addable auto y) {
      |      ^~~~
concept_auto.cc:10:6: note:   template argument deduction/substitution failed:
concept_auto.cc:10:6: note: constraints not satisfied
concept_auto.cc: In substitution of 'template<class auto:18, class auto:19>  requires (Addable<auto:18>) && (Addable<auto:19>) auto add2(auto:18, auto:19) [with auto:18 = Foo; auto:19 = Foo]':
concept_auto.cc:17:20:   required from here
concept_auto.cc:8:9:   required for the satisfaction of 'Addable<auto:18>' [with auto:18 = Foo]
concept_auto.cc:8:19:   in requirements with 'T a', 'T b' [with T = Foo]
concept_auto.cc:8:42: note: the required expression '(a + b)' is invalid
    8 | concept Addable = requires(T a, T b) { a + b; };

2.编译器支持

需要GCC(10.0+),Clang(10.0+),编译选项:-std=c++20/-std=c++2a

https://en.cppreference.com/w/cpp/compiler_support

3.总结

自C++20提供的concept之后,我们不再需要enable_if/SFINAE的机制、函数重载来做一些模版约束检查了,使用concept可以帮你搞定这个操作,它提供了一种更清晰和强大的模板参数约束机制,使得模板代码更易于编写、理解和维护。通过在编译时进行类型检查,它有助于提高代码的稳健性和可读性。

源码获取👇:

C++20:从0到1学懂concept,c++20

C++20:从0到1学懂concept,c++20


往期回顾:

热度更新,手把手实现工业级线程池

C++ 多值返回:从版本1到版本6秒杀
文章来源地址https://www.toymoban.com/news/detail-815971.html

到了这里,关于C++20:从0到1学懂concept的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Java Polymorphism Concept

    这个多态看中文确实有点费解,多态的英文是Polymorphism,它的翻译含义是: n. 多态性 (可以看出是比较宽泛的) n. 多型现象 从翻译也看不出啥, 我举一个生活中的例子来引入多态: 生活中有很多常见的物体具有多态性。例如,一张纸可以用来写字、画画,也可以用来包裹东

    2024年02月12日
    浏览(38)
  • Prometheus - Concept

    Prometheus - Concept

    一 Prometheus 是什么         Prometheus 是一个开源的 监控和报警系统 。该系统内置和基于时间序列地抓取、存储、查询、绘图数据、报警。         现在是一个开源项目,继 K8S 后的第二个云原生计算基金会的托管项目,可见其火爆程度。 二 Prometheus 的特征 Prometheus 的

    2024年02月13日
    浏览(5)
  • Concepts基本概念

    本文档参考backtrader官方文档,是官方文档的 完整中文翻译 ,可作为backtrader中文教程、backtrader中文参考手册、backtrader中文开发手册、backtrader入门资料使用。 查看本专栏完整内容,请访问:https://blog.csdn.net/windanchaos/category_12350607.html

    2024年02月07日
    浏览(9)
  • Set Concept

    Set Concept

    集合(Set)就是一种用来装事物的容器(或者称为结构),它所装的东西叫元素(Element)。集合这个容器的逻辑性很强,可以说是现在比较严谨的工具,不熟悉逻辑符号和逻辑思想的话,可以先去了解一下。 集合里的元素,它们可以是任何类型的数学对象:数字、符号、变

    2024年02月11日
    浏览(6)
  • 【Git Concept】

    Git is designed for many guy’s corporation concurrently. CVCS:中央分布式控制系统,typical:SVN DVCS:Git; Git accounts up 74%,and svn accounts up 22%; Difference:CVCS means 中央版本控制系统必须同时存在服务端和客户端,在业务流程过程中,服务端必须存在,所有请求必须经过服务端的处理。 DVCS:主要是将

    2024年02月17日
    浏览(23)
  • C++ 模板 using, Concept

     用IteratorMemberFunctionT(T::begin)从T中获取成员函数的函数指针 使用decltype获取类型,判断该类型是否为一个成员函数,不包含 begin 就会违反这个约束

    2024年02月11日
    浏览(13)
  • Java Polymorphism Concept —— OOP

    这个多态看中文确实有点费解,多态的英文是Polymorphism,它的翻译含义是: n. 多态性 (可以看出是比较宽泛的) n. 多型现象 从翻译也看不出啥, 我举一个生活中的例子来引入多态: 生活中有很多常见的物体具有多态性。例如,一张纸可以用来写字、画画,也可以用来包裹东

    2024年02月12日
    浏览(11)
  • C++ concept的概念和使用

    concept 这套语法优化了模板编程,替代了原来的SFINAE编程模式,通过给模板类参数加入限制条件,使得代码可读性更强、编译更快、错误提示更容易理解。 SFINAE 是\\\"Substitution Failure Is Not An Error\\\"的简称。 模板实例化时类型推演失败不会报错,而是当成一个语言特性。利用模板通

    2024年02月09日
    浏览(7)
  • 小白学懂分频器(二)

    小白学懂分频器(二)

    分频:简单来说,二分频后的方波一个周期为标准方波高低电平循环两个周期,四分频为4个周期。 分频后的时钟周期为原来的n倍,即为n分频。   频率和周期的关系:f=1/T (1)简单的计数器   计数器实质是对输入的驱动时钟进行计数,在某种意义上讲,等同于对时钟进

    2024年02月16日
    浏览(9)
  • R语言【taxlist】——add_concept(),update_concept():将新的分类概念添加到 taxlist 类对象中

    Package  taxlist  version 0.2.4 将新的分类概念添加到现有的 taxlist 对象中的可替换方法。 参数【taxlist】:一个 taxlist 对象。 参数【TaxonName】:具有新分类单元概念的接受名的字符向量。 参数【...】:方法之间传递的进一步参数。 参数【insert_view】:一个数字(整数)向量,指示要

    2024年01月25日
    浏览(11)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包