conceptC++

http://www.generic-programming.org/faq/?category=conceptcxx

Checking Concept Without Concepts in C++

By Anthony Williams, September 22, 2010

1 Comment

Get the benefits of C++0x but without the work

Anthony Williams is author of the book C++ Concurrency in Action and of the just::thread C++0x thread library. He can be contacted at anthony.ajw@gmail.com


"Concepts" was set to be one of the major selling points of the new C++0x standard, until it was removed from the draft in July 2009. The "Concepts" feature promised better compile-time checking of templates, and the ability to overload functions based on whether or not their parameters supported specific templates.

In this article I look at some techniques for obtaining some of the benefits of concepts, with the facilities we already have in C++. Source code that illustrates these techniques is listed at the end of this article.

Basic Tools

The basic tools we need for concept checking and concept-based overloading are static_assertenable_if, and a particular property of template instantiation dubbed SFINAE (short for "Substitution Failure Is Not An Error").static_assert is useful for the checking, enable_if for the overloading. SFINAE is the mechanism that makesenable_if work, and can be used to write additional checks for testing with static_assert or enable_if.

static_assert is a new C++0x language feature, and is available in the latest versions of some compilers, such as Microsoft Visual Studio 2010 and g++ 4.3 or later. It allows you to specify a boolean constant expression and an error message -- if the constant expression evaluates to False at compile time then the compilation fails and the specified error message is output. For example, this simple program compiled with gcc -std=c++0x:

    int main()
{
static_assert(false,"your message goes here");
}

yields this error message:

    test.cpp: In function 'int main()'
test.cpp:3: error: static assertion failed: "your message goes here"

The constant expression can of course depend on template parameters, and that's where the checking comes in: if you put a static_assert in a template then the compilation will fail if the specified condition is not True, and the error message will be a lot clearer than what you would get otherwise.

static_assert works really well with the standard type traits -- you can assert that a given type is convertible tobool, or is derived from a particular base class, or is a POD. Of course, you can also define your own traits for any characteristic that you can build a test for. Later in this article I'll show how you can build a test for the presence of particular member functions.

You could, for example enforce the constraint that a particular template parameter is a POD type, so you can copy instances with memcpy:

    template<typename T>
void copy(T const* source,T* dest,unsigned count)
{
static_assert(std::is_pod<T>::value,"T must be a POD");
memcpy(dest,source,count*sizeof(T));
}
is

If you try and use this copy function with a non-POD type such as std::string, then you will get a compilation error.

The Boost Concept Check Library provides an alternative to static_assert for checking for concept conformance. Just like static_assert, it generates compiler errors if the concept is not matched. However, it may provide an easier way of specifying the constraints than plain static_assert if concept-based overloading is not required.

Whereas static_assert is all about hard and fast requirements, enable_if is about choices. You use it to enable certain function overloads if and only if a given property is True. This enables you to specify different versions of an algorithm based on the properties of the template parameters.

For example, you could use enable_if to use memcpy when copying PODs, rather than copying each element individually:

    template<typename T>
typename std::enable_if<std::is_pod<T>::value,void>::type
copy(T const* source,T* dest,unsigned count)
{
memcpy(dest,source,count*sizeof(T));
} template<typename T>
typename std::enable_if<!std::is_pod<T>::value,void>::type
copy(T const* source,T* dest,unsigned count)
{
for(unsigned i=0;i<count;++i)
{
*dest++=*source++;
}
}

enable_if is quite simple in and of itself -- if the first template parameter (which must be a boolean constant expression) evaluates to true then the nested "type" member is a typedef to the second template parameter. If the the first parameter evaluates to False then there is no nested "type" member.

The SFINAE rules mean that if enable_if<some-expression,some-type>::type is used in the signature of a function template (as here), then that function overload is discarded if some-expression is False -- that overload is only enabled if some-expression is True.

SFINAEThe basic premise of SFINAE is that when the compiler is deducing the template parameters for a function template from a function call, if the deduced parameters would make the signature invalid then that function template is not considered for overload resolution rather than resulting in a compilation failure. There are limits, and some template instantiation errors will still cause compilation failure, but this is the basic principle that makes enable_if work.

For example, given the function template:

    template<typename T>
typename T::type foo(T t)
{}

If you try and call foo(3), then T is deduced to be int. The type int does not have a member called type. The instantiation is therefore invalid when substituting "int" as the template parameter. By the SFINAE rule, this is not an error: instead the overload is ignored. If there is another overload of foo that can match foo(3), then that will be chosen instead. Of course, if there are no other overloads or none of the others match, then you still get a compilation error.

SFINAE does not work if the required instantiation of the function template depends on the instantiation of another template and that instantiation fails; for example:

    template<typename T>
struct bar
{
typedef typename T::type type;
}; template<typename T>
typename bar<T>::type foo2(T t)
{
// ...
}

If you call foo2(3), then T is again deduced as int. However, bar<int> cannot be instantiated, since int does not have a member called type. This is a failure in the instantiation of bar<int> , not in the instantiation of foo2<int>, so is a real compiler error, and will not be ignored by SFINAE.


Availability of Features

Newer compilers (such as gcc 4.3 or later, and Microsoft Visual Studio 2010) are starting to provide C++0x features. static_assert is one of the most common C++0x language features added to compilers, but for those compilers that don't have static_assert, you can emulate it with BOOST_STATIC_ASSERT.

enable_if on the other hand is purely a library facility, and was part of the C++ Technical Report 1. As such it is even more widely available as part of the library supplied with compilers. For those compilers that don't provide their own version, it is also available as part of the Boost Library.

--A.W.

static_assert enable_if 模板编译期检查的更多相关文章

  1. 通过宏封装实现std::format编译期检查参数数量是否一致

    背景 std::format在传参数量少于格式串所需参数数量时,会抛出异常.而在大部分的应用场景下,参数数量不一致提供编译报错更加合适,可以促进我们更早发现问题并进行改正. 最终效果 // 测试输出接 ...

  2. 数值类型中JDk的编译期检查和编译期优化

    byte b1 = 5;//编译期检查,判断是否在byte范围内 byte b2 = 5+4;//编译期优化,相当于b2=9 byte b3 = 127;//编译通过,在byte范围内 byte b4 ...

  3. C++17尝鲜:编译期 if 语句

    Constexpr If(编译期 if 语句) 以 if constexpr 打头的 if 语句被称为 Constexpr If. Constexpr If 是C++17所引入的新的语法特性.它为C+ ...

  4. 简单的说一下:tarits技法就是一种模板元编程,起可以将本来处于运行期的事拉到编译期来做,增加了运行效率。 看以非模板元编程的例子,就是前面的那个例子:

    void adance(std::list<int>::iterator& iter, int d) { if(typeid(std::iterator_traits<std ...

  5. 读书笔记 effective c++ Item 41 理解隐式接口和编译期多态

    1. 显示接口和运行时多态 面向对象编程的世界围绕着显式接口和运行时多态.举个例子,考虑下面的类(无意义的类), class Widget { public: Widget(); virtual ~W ...

  6. c++ 编译期与运行期

    分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 转自:http://h ...

  7. 《深入理解Java虚拟机》-----第10章 程序编译与代码优化-早期(编译期)优化

    概述 Java语言的“编译期”其实是一段“不确定”的操作过程,因为它可能是指一个前端编译器(其实叫“编译器的前端”更准确一些)把*.java文件转变成*.class文件的过程;也可能是指虚拟机的后端运 ...

  8. java编译期优化

    java语言的编译期其实是一段不确定的操作过程,因为它可以分为三类编译过程: 1.前端编译:把.java文件转变为.class文件 2.后端编译:把字节码转变为机器码 3.静态提前编译:直接把*.ja ...

  9. C++编译期多态与运行期多态

    前言 今日的C++不再是个单纯的"带类的C"语言,它已经发展成为一个多种次语言所组成的语言集合,其中泛型编程与基于它的STL是C++发展中最为出彩的那部分.在面向对象C++编程中, ...

随机推荐

  1. node 解析图片二维码的内容

    const {readFile, readFileSync} = require('fs'); const decodeImage = require('jimp').read; const qrco ...

  2. 家庭记账本之微信小程序(三)

    继上篇注册阶段后,经过查阅资料学习后,以下介绍开发阶段 1.登录微信公众平台就能在菜单“开发”---“基本配置”中看到小程序的AppID了. 小程序的 AppID 相当于小程序平台的一个身份证,后续你 ...

  3. 组件式开发(Vue)

    什么是组件式开发: 组件式开发就是将单个组件组合起来,形成一个大的组件进行页面的开发完成 什么是复合型组件: 复合型组件就是将相同的功能写成一个公用的组件(单元组件),供其他组件使用,就类似于后台开发 ...

  4. Gis数据处理2 ---8.18

    1空间参考: 了解大地水准面,参考椭球体,基准面的概念 以及之间的关系   基准面描述的是参考椭球体中心 跟地心的关系   我们常说的北京54.西安80.CGCS2000,实际上指的是我国的三个大地基 ...

  5. 【js】字符串反转可实现的几种方式

    方式1: 这种方式比较简单,推荐使用 字符串转数组,反转数组,数组转字符串. split(""):根据空字符串拆分数组 reverse():数组反转元素位置 join(" ...

  6. ci 配置ckeditor + ckfinder 无图片上传按钮

    一:配置路径有问题 {$base_url}assets/js/editor/ckfinder/ckfinder.html  --> http://www.cnblogs.com/assets/j ...

  7. jQuery 筛选器2

    jQuery 筛选器2 // 由于$()只能输入字符串$('#li:eq(1)'),可通过.eq()来传入. // 获取this标签中的指定属性 $(this).eq(1) // 获取第一个元素 $( ...

  8. Shell 变量、脚本参数

    定义变量:可将脚本或者多个命令定义成一个变量. ()格式n(){脚本命令}. 脚本常用参数 命令:seq –w 0 23 #以01开头往上的. 命令:echo –ne #输出n换行,e扩展. 命令:b ...

  9. activiti5/6 系列之--BpmnModel使用

    BpmnModel对象,是activiti动态部署中很重要的一个对象,如果BpmnModel对象不能深入的理解,那可能如果自己需要开发一套流程设计器,使用bpmn-js使用前端或者C/S展现流程流转而 ...

  10. 经典算法问题的java实现 (二)

    原文地址: http://liuqing-2010-07.iteye.com/blog/1403190   1.数值转换(System Conversion) 1.1 r进制数   数N的r进制可以表 ...