C++0x 概念和 Boost 概念检查库 (BCCL) 之间有什么区别?

发布于 2024-08-03 21:31:42 字数 224 浏览 6 评论 0原文

Concepts 并未制定 C++0x 标准,但 Boost 仍然提供 Boost 概念检查库 (BCCL)。我猜想 BCCL 并没有涵盖 C++0x 标准中的所有内容。 BCCL 和建议的 C++0x 解决方案有什么区别?

Concepts didn't make the C++0x standard, but Boost still provides The Boost Concept Check Library (BCCL). I guess that BCCL doesn't cover everything that was meant to into the C++0x standard. What is the difference between BCCL and the proposed C++0x solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

无戏配角 2024-08-10 21:31:42

检查模板定义

A big difference of concepts to these manual solutions is that concepts allow the definition of a template to be type-checked without doing anything special. The concept check library allows only the *use* of it to be type checked (unless you manually write test-instantiation types or make use of provided types for standard cases, see below). Example:

template<typename InputIterator>
int distance(InputIterator a, InputIterator b) 
{ return b - a; }

您现在可以在该模板中添加概念检查和特征,但是在编写该模板后您将永远不会收到错误 - 因为标准允许编译器延迟编译模板直到实例化。为了进行检查,您必须编写“原型”类,其中包含接口所需的确切操作,然后人为地实例化它们。

阅读 BCCL 的文档,我发现它已经包含了常见的原型,例如“默认可构造”。但是,如果您编写自己的概念,则还必须提供自己的原型,这并不容易(您必须准确找到类型必须提供的最小功能)。例如,如果您的原型包含 operator-,那么使用该(不正确的)原型测试您的模板将会成功,尽管这些概念不需要这样的运算符。

被拒绝的概念提案会根据指定和隐含的要求自动为您创建原型(参数中使用的指针类型 T* 将暗示 TT 的 PointeeType 要求代码>,例如)。您不必关心这些东西 - 当然,除非您的模板定义包含类型错误。

检查语义要求

考虑此代码,使用假设的概念检查

template<ForwardIterator I>
void f(I a, I b) {
  // loop two times!
  loopOverAToB(a, b);
  loopOverAToB(a, b);
}

BCCL 手册说,检查语义要求。仅检查语法要求和类型。考虑一个前向迭代器:存在可以在多遍算法中使用它的语义要求。仅语法检查无法测试此要求(想想如果流迭代器意外通过该检查会发生什么!)

在被拒绝的提案中,您必须显式地将 auto 放在前面概念定义以使编译器在语法检查后标记成功。如果未指定 auto,则类型必须显式定义概念图来表示它支持该概念。因此,流迭代器永远不会被用来通过 ForwardIterator 检查。

语法重新映射

This was another feature. A template such as

template<InputIterator I>
  requires OutputStreamable<I::value_type>
void f(I a, I b) {
  while(a != b) std::cout << *a++ << " ";
}

如果用户提供一个概念图来教导编译器如何取消引用整数,以及整数如何满足 InputIterator 概念,则可以像下面这样使用。

f(1, 10);

这是基于语言的解决方案的好处,我相信 BCCL 永远无法解决。

基于概念的重载

On a quick read of BCCL, i can also not spot anything that allows this to happen. A concept matching failure seems to cause a hard compilation error. The rejected proposal allows the following:

template<ForwardIterator I>
I::difference_type distance(I a, I b) {
  I::difference_type d = 0; while(a != b) ++a, ++d;
  return d;
}

template<RandomAccessIterator I>
I::difference_type distance(I a, I b) {
  return b - a;
}

如果一个类型可以与两个模板一起使用,那么将使用第二个模板,因为它更专业:RandomAccessIterator 改进了 ForwardIterator 概念。

Checking the template definition

A big difference of concepts to these manual solutions is that concepts allow the definition of a template to be type-checked without doing anything special. The concept check library allows only the *use* of it to be type checked (unless you manually write test-instantiation types or make use of provided types for standard cases, see below). Example:

template<typename InputIterator>
int distance(InputIterator a, InputIterator b) 
{ return b - a; }

You may now sprinkle that template with concept checks and traits, but you will never get an error after writing that template - because the Standard allows the compiler to delay compiling the template until instantiation. For checking, you have to write "archetype" classes, which contain exactly those operations that are required by the interface and then instantiate them artificially.

Reading the documentation of BCCL, i found it already includes the common archetypes like "default constructible". But if you write your own concepts, you will have to also provide your own archetypes, which isn't easy (you have to find exactly the minimal functionality a type has to provide). For example, if your archetype contains a operator-, then the test of your template with that (incorrect) archetype will succeed, although the concepts don't require such an operator.

The rejected concepts proposal creates archetypes for you automatically, based on the requirements that were specified and that were implied (a pointer type T* used in a parameter will imply the PointeeType requirement for T, for example). You don't have to care about this stuff - except of course when your template definition contains a type error.

Checking semantic requirements

Consider this code, using hypothetical concept checks

template<ForwardIterator I>
void f(I a, I b) {
  // loop two times!
  loopOverAToB(a, b);
  loopOverAToB(a, b);
}

The BCCL manual says that semantic requirements are not checked. Only syntax requirement and types are checked. Consider a forward iterator: There exists the semantic requirement that you may use it in multi-pass algorithms. Syntax-checking only won't be able to test this requirement (think about what happens if a stream iterator accidentally would pass that check!)

In the rejected proposal, you had to explicitly put auto in front of concept definitions to make the compiler flag success after syntax-checking. If auto wasn't specified, then a type explicitly had to define a concept map to say it supports that concept. A stream iterator would thus never be taken to pass a ForwardIterator check.

Syntax remapping

This was another feature. A template such as

template<InputIterator I>
  requires OutputStreamable<I::value_type>
void f(I a, I b) {
  while(a != b) std::cout << *a++ << " ";
}

Can be used like the following, if the user would provide a concept map that teaches the compiler how to dereference an integer, and thus how an integer satisfies the InputIterator concept.

f(1, 10);

This is the benefit of a language-based solution, and cannot be solved by BCCL ever, i believe.

Concept based Overloading

On a quick read of BCCL, i can also not spot anything that allows this to happen. A concept matching failure seems to cause a hard compilation error. The rejected proposal allows the following:

template<ForwardIterator I>
I::difference_type distance(I a, I b) {
  I::difference_type d = 0; while(a != b) ++a, ++d;
  return d;
}

template<RandomAccessIterator I>
I::difference_type distance(I a, I b) {
  return b - a;
}

If a type could be used with both templates, then the second template would be used, because it's more specialized: RandomAccessIterator refines the ForwardIterator concept.

跨年 2024-08-10 21:31:42

C++0x概念特性将是一个核心语言特性,其整个过程将由编译器完成。

Boost Concept Check Library 具有几乎相同的功能,但用 C++ 和宏作为库编写来模拟某些功能。它无法完成最终语言功能所需的所有操作(取决于最终功能定义),但可以为模板类型检查(以及其他编译时检查)提供一些等效的解决方案。

正如所建议的,由于 C++0x 概念是一种语言功能,因此它将允许提供更优雅的语义并允许编译器使用程序当前无法使用的信息,从而允许在编译时出现更详细或智能的错误(如概念首先目的是允许在模板中进行抽象类型检查)。

The C++0x concept feature would be a core language feature, whose whole process would be done by the compiler.

The Boost Concept Check Library is the almost same feature but written in C++ and macro as a library to simulate some of the feature. It cannot do all that would be required in the final language feature (depending on the final feature definition) but provide some equivalent solutions for template type checking (and other compile time checks).

As suggested, as the C++0x concept is a language features, it would allow to provide more elegant semantic and allow compiler to use informations not currently available to the program, allowing more detailed or intelligent errors at compile time (as the concept first purpose is to allow abstract type check in templates).

岁月无声 2024-08-10 21:31:42

免责声明:即使我已经安装了 Boost,我也无法在过去 30 分钟内成功使用 BCCL。根据 Boost 1.37 的 BCCL 文档,您在下面看到的示例看起来不错,但不起作用。我想这算是劣势吧。

使用 BCCL,您只能得到静态断言之类的东西,而核心语言概念功能提供了完整的模块化类型检查,并且能够防止某些函数模板参与重载解析。使用本机概念,编译器可以立即检查受约束模板的主体,而 BCCL 不会让编译器检查这方面的任何内容。您必须使用“arche type”参数手动实例化模板,以查看模板是否使用任何不可用的操作(例如,前向迭代器上的运算符--)。

至于重载解析,这里有一个例子:

template<typename Iter>
void foo(Iter,Iter) {
   BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}

void foo(long, int);

int main() {
   foo(2,3); // compile-time error
}

模板是更好的匹配,因为非模板需要从 int 到 long 的转换。但实例化失败,因为 int 不是迭代器。您会收到一条很好的错误消息来解释它,但这并不是很令人满意,不是吗?使用本机概念,您可以在此处编写

template<typename Iter>
  requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

,函数模板将不会参与重载决策,因为不满足 T=int 的要求。伟大的!

我们仍然可以使用 SFINAE 技巧来约束函数模板。 C++0x 将 SFINAE 扩展到表达式,并与我们可以编写的函数模板的 decltype 和默认模板参数一起

template<typename T> T&& make();

template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

在这种情况下,模板参数推导将默默失败,因为编译器不知道表达式 ∗make() 应该是(我们不能取消引用 int)。通过一些模板元编程和一些宏,我们可以非常接近以可读的方式对模板参数施加任意结构约束。假设 REQUIRES 和 RandomAccessIterator 具有适当的定义,情况可能如下:

template <typename Iter
  REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}

HTH,
S

Disclaimer: I wasn't able to successfully use BCCL within the last 30 minutes even though I had Boost installed already. The example you see below looks OK according to the BCCL documentation of Boost 1.37 but didn't work. I guess this counts as disadvantage.

With BCCL you only get something like static assertions whereas the core language concept feature provides full modular type checking and is able to prevent some function template from participating in overload resolution. With native concepts the body of a constrained template can be checked immediately by the compiler whereas BCCL doesn't make the compiler check anything in that respect. You have to manually instantiate your template with "arche type" parameters to see if the template uses any operations that are not available (for example, operator-- on forward iterators).

As for overload resolution here's an example:

template<typename Iter>
void foo(Iter,Iter) {
   BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}

void foo(long, int);

int main() {
   foo(2,3); // compile-time error
}

The template is a better match because the non-template requires a conversion from int to long. But instantiation fails because int is not an iterator. You get a nice error message explaining it but that's not very satisfying, is it? With native concepts you could have written

template<typename Iter>
  requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

Here, the function template will not take part in overload resolution because the requirement for T=int is not satisfied. Great!

We still get to constrain function templates with the SFINAE trick. C++0x extends SFINAE to expressions and together with decltype and default template arguments for function templates we can write

template<typename T> T&& make();

template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

In this case, template argument deduction will fail silently because the compiler doesn't know what the type of the expression ∗make<Iter>() is supposed to be (we can't dereference an int). With a bit of template metaprogramming and some macros we can get pretty close to imposing arbitrary structural constraints on template parameters in a readable way. Here's what it might look like assuming appropriate definitions for REQUIRES and RandomAccessIterator:

template <typename Iter
  REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}

HTH,
S

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文