它试图做什么样的邪恶魔法!?!
我正在听Q&A session with herb sutter,有一个问题是关于概念的。 Herb提到它使编译器变慢(虽然源保持不变)并且该部分明显大于模板部分。
为什么这样做?我在哪里可以找到关于概念的文档?
答案 0 :(得分:10)
首先,赫伯没有说概念本身使编译速度变慢。他说,对C ++标准库进行概念化使得使用C ++标准库的任何代码都编译得更慢。
原因归结为几件事。
1:约束模板需要编译时间。
当您声明这样的类时:
template<typename T> class Foo {...};
编译器简单地解析Foo并且做得很少。即使使用两阶段查找,编译器也不会在类Foo的编译中做很多事情。当然,它存储它以供日后使用,但初始传递相对较快。
使用概念约束模板时:
template<ConceptName C> class Foo {...};
编译器必须做一些事情。必须事先检查类型C
的每次使用是否符合概念ConceptName
。这是编译器在实例化时间之前推迟的额外工作。
检查的概念越多,用于验证类型与概念匹配的编译时间就越多。
2:标准C ++库使用了很多概念。
查看迭代器概念的数量:输入,输出,转发,双向,顺序,连续。委员会正在考虑将它们分解为更多。对于不同的迭代器概念,许多算法都有多个版本。
这不包括范围概念(除了输出之外,每种迭代器概念都有一个),std :: string的字符概念以及各种其他类型的东西。所有这些都必须进行编译和检查。
真正需要快速实现的概念是模块。编译器生成包含一系列预先检查符号的模块文件的能力,然后直接加载该文件而无需经过标准编译过程。直接从解析到符号创建。
请记住:对于每个.cpp文件#include,编译器必须读取该文件并进行编译。即使文件每次执行此操作都是相同的事情,它仍然必须尽职尽责地读取文件并进行处理。如果我们谈论的是概念化std::vector
,它必须对模板进行所有概念检查。它仍然必须执行编译时执行的所有标准符号查找。等等。
想象一下,如果编译器不必这样做。想象一下,它是否可以直接从磁盘加载一堆符号和定义。根本没有编译;只需为其他代码引入符号和定义即可使用。
这就像预编译头文件更好。预编译头文件仅限于每个.cpp文件一个,而您可以使用任意数量的模块。
可悲的是,模块在C ++ 0x的过程中很早就被淘汰了。如果没有模块,使用概念约束标准库总是比无约束版本编译得慢。
请注意,Herb误解了模块的用途(并不难,因为该功能的大多数初始概念都是他所谈到的内容:跨平台DLL等)。它们的核心基本目的是帮助编译时间,而不是使跨平台DLL工作。模块本身也不是跨平台的。
答案 1 :(得分:0)
您可能会在ConceptsGCC website上找到有用的资源。这就是他们正在构建的编译器(分支GCC),以查看概念(原谅双关语)是否可行。
我认为费用来自于必须对各种语言结构执行彻底,无处不在和递归的有效性检查,并且鉴于您可以指定一组非常丰富的约束,检查那些可能会非常昂贵。
有点像异常规范的噩梦版本!
答案 2 :(得分:0)
我是来自Incredibuild的Uval,因为这个问题已经很久了(从2011年开始),并且在撰写本文时(2020年)最近发布了一些概念,所以我想澄清两点,只是为了避免误导人们或阻止他们使用概念。
曾经被考虑的概念和现在发布的概念是完全不同的存在。 C ++ 20中发布的概念也被称为“精简概念”,因为与概念的初始设计相比,它们包含的功能有所减少。那么,从概念中删除了什么?
主要区别在于,概念的主要设计不仅用于检查模板使用的正确性,而且还用于检查此模板定义的正确性。例如,假设您有一个类型为list-comprehension
的模板,该模板需要具有成员函数ids = [item['track']['id'] for items in response for item in items['items']]
。您可以想象这样一个受约束的函数模板:
write_to_file
现在,使用概念的初始设计,功能模板def write_to_file(ids):
with open('id.txt', 'a') as file:
file.write("\n".join(ids) + '\n')
response = [{'items': [{'track': {'id': '6gaeewGjNiQhWOUkWRSLTa'}}, {'track': {'id': '0TK2YIli7K1leLovkQiNik'}},
{'track': {'id': '6v0lAdFF4haL8xjBIUjtOw'}}, {'track': {'id': '443cnFF139Ql85enXOTWEu'}},
{'track': {'id': '7bBIBrSmHvdMlTC7b9p8Vq'}}, {'track': {'id': '50Ud3ecSw6hsVlmAECRuBV'}},
{'track': {'id': '0sjSgTluUJIdqfyRe4EQ4U'}}, {'track': {'id': '2TfcMUAZ4vH4fTA9eF53v2'}},
{'track': {'id': '0YOUE1TlkgSbkNvWkZkEVp'}}, {'track': {'id': '5itHWf2i5yo9PM9bgq4hmP'}},
{'track': {'id': '3AKm9sJmK0v3wUQYSLuze7'}}]}]
ids = [item['track']['id'] for items in response for item in items['items']]
print(ids)
# ['6gaeewGjNiQhWOUkWRSLTa', '0TK2YIli7K1leLovkQiNik', '6v0lAdFF4haL8xjBIUjtOw', '443cnFF139Ql85enXOTWEu',
# '7bBIBrSmHvdMlTC7b9p8Vq', '50Ud3ecSw6hsVlmAECRuBV', '0sjSgTluUJIdqfyRe4EQ4U', '2TfcMUAZ4vH4fTA9eF53v2',
# '0YOUE1TlkgSbkNvWkZkEVp', '5itHWf2i5yo9PM9bgq4hmP', '3AKm9sJmK0v3wUQYSLuze7']
write_to_file(ids)
的定义将是不正确的,因为我们使用的是Animal
成员函数,该成员函数不是必需表达式的一部分。使用精简的C ++ 20概念,此概念定义就可以了。编译器将不会检查make_sound
函数模板的正确性,因为在一个轻量级的概念世界中,开发人员需要正确指定类型的要求。
这种差异可以在编译时间上产生很大的差异。在2016年,有两篇论文考虑了概念是否进入C ++ 17的原因:
“Why I want Concepts, and why I want them sooner rather than later”和
“Why I want Concepts, but why they should come later rather than sooner.”
两者都没有考虑过性能,因此可以很好地说明当时不是问题。
此外,当前的概念设计可能具有一些性能优势。根据{{3}}的说法,编译速度最慢的是SFINAE,因为它至少需要尝试(通常)实例化大量类型,然后稍后再放弃它们。概念(取决于实现方式)可能不需要实例化任何模板,实际上可能最终会带来性能优势。