CppCon - Modern Template Metaprogramming 杂记

2014年底才看到github和channel9上有CppCon2014的视频和资料,顿时激动不已。最近小生也一直在研习CppCon2014中令人兴奋的内容。这篇鄙文就是小生学习了《Modern Template Metaprogramming》之后,有对Unevaluated Operands更深刻的理解,有感而写。

C++98标准中的Unevaluated Operands,只有sizeof操作符。C++11又引入了decltype,typeid和noexcept。Unevaluated Operands不会有求值的计算,即使是在编译期间。这意味着Unevaluated Operands中的表达式甚至不会生成具体的C++代码,操作符中的表达式仅需要声明而无需定义。在模板元编程中,我们有时候经常仅需要对catch a glimpse of一个表达式,而这些操作符都是很好的工具。

下面我们以std::is_copy_assignable为例来看看Unevaluated Operands的威力。

namespace cpp11
{
    template <typename T>
    struct is_copy_assignable
    {
    private:
        template <typename U, typename = decltype(std::declval<T&>() = std::declval<T const&>())>
        static std::true_type try_assign(U&&);

        static std::false_type try_assign(...);

    public:
        using type = decltype(try_assign(std::declval<T>()));
    };
}

我们以c++11的版本开始。这个std::is_copy_assignable的实现原理就是SFINAE。给定一个C++类型T,如果T存在一个有效的operator=,那么成员模板函数try_assign的第二个模板参数中decltype里面的表达式将是well formed,decltype将会推导出这个表达式的类型。而第二个非模板的普通成员函数try_assign的形参使用" ..." 是因为这种情况是最差匹配的情况,is_copy_assignable需要优先匹配成员模板函数try_assign。元函数返回type时,再次使用了decltype,这里是真正的点睛之笔。由于decltype无需求值,因此try_assign无需一个函数体。std::declval是C++11引入universal reference的一个附属品,它也是一个模板函数同样没有函数体,返回值的类型是一个T&&(universal reference),try_assign中使用std::declval可以让编译器知道我们传递给这个函数的参数类型,但没有真正的去构造一个对象,就好像是假设我传递一个实参给try_assign. 在Unevaluated Operands中的表达式如果是一个函数调用,那么std::declval是一个非常好的工具,姑且可以将它当做一个惯用法。那么如果这个T不存在一个有效的operator=,编译器将使用SFINAE选择普通的成员函数。最后元函数返回的type在T::operator= 存在的情况下是std::true_type,反之则是false_type.

在c++98的年代,Unevaluated Operands只有sizeof,那么怎么实现呢。同使用下面这个例子或者类似的实现方法。

namespace cpp98
{
    namespace detail
    {
        template <typename T>
        T declval();
    }

    template <typename T>
    struct is_copy_assignable
    {
    private:

        typedef char One;
        typedef struct { char a[2]; } Two;

        template <int N = sizeof(detail::declval<T&>() = detail::declval<T const&>())>
        static One try_assign(int);

        static Two try_assign(...);

    public:

        typedef typename std::conditional<sizeof(try_assign(0)) == sizeof(One),
            std::true_type, std::false_type>::type type;
    };
}

sizeof只能推导出类型的大小,因此try_assign不能再返回std::true_type和st::false_type,而是返回了两个大小不同的类型。实现原理依旧是SFINAE,而元函数返回的类型是根据try_assign返回类型的大小来决定导出std::true_type还是std::false_type.由于cpp98没有右值引用,所以这里自己实现了一个declval的替代版。在c++98的版本中,元函数的类型计算都是由sizeof来驱动的。c++11引入了更多的非求值操作符后,元编程确实方便了不少。

在《Modern Template Metaprogramming》这篇演讲中还有一个非常有趣的模板工具,就是void_t.  它的实现可能如下代码所示

template <typename ... Args>
struct make_void
{
    typedef void type;
};

template <typename ... Args>
using void_t = typename make_void<Args...>::type;

或者,

template <typename ... Args>
using void_t = void;

让我们先来看看使用void_t来实现我们的is_copy_assignable.

namespace cpp1y
{
    template <typename ... Args>
    struct make_void
    {
        typedef void type;
    };

    template <typename ... Args>
    using void_t = typename make_void<Args...>::type;

    template <typename T, typename = void>
    struct is_copy_assignable : std::false_type
    {};

    template <typename T>
    struct is_copy_assignable<T, void_t<decltype(std::declval<T&>() = std::declval<T const&>())>> : std::true_type
    {
    };
}

利用void_t实现的版本又要比使用SFINAE的版本实现简单许多。给定一个C++类型T,传递到元函数is_copy_assignable中,由于这个版本的元函数有两个参数,那么is_copy_assignable<T>被推导为is_copy_assignable<T,void>. 接下来,编译要检查是否有more specialized的特化版本。如果T有一个有效的operator=,那么is_copy_assignable的特化版本中的第二个参数中的decltype推导的表达式是well formed,那么decltype计算类型并与void_t共同推导出void. 由于特化版本是more specialized,因此编译器选择这个特化版本,并调用元函数std::true_type。反之,如果T没有一个有效的operator=,那么decltype推导的表达式无效,因此这种情况下的is_copy_assignable没有特化版本并调用元函数std::false_type。这里要注意的是,第二个默认的模板参数必须要以void为缺省的实参类型,因为void_t实际上就是void,才能让编译器找到特化的版本。换而言之,缺省的类型要与void_t一致,如果实现一个类似的int_t,那么这个缺省的类型应该是int.

小生在MSVC12实验的void_t版本一直有问题,还有更简单版本的void_t实现在MSVC和gcc的编译器上都不对,根据这篇演讲的说法是还需要一个提案,详见CWG 1558, treatment of unused arguments in an alias template specialization.  错误都一样,无论T是否有可用的operator=,这个元函数始终选择特化版本,即都是true_type,可能是因为void_t模板没有使用任何模板参数的原因吧,还需要进一步的研究。

这是最后的实验代码和运行结果如下:

http://ideone.com/ZUCvhY

时间: 2024-11-08 02:04:19

CppCon - Modern Template Metaprogramming 杂记的相关文章

Template Metaprogramming with Modern C++: Introduction

Template Metaprogramming with Modern C++: Introduction Posted on September 10, 2014 by Manu Sánchez Template Metaprogramming with Modern C++: Introduction Any sufficiently complex C++ code is indistinguishable from trolling Arthur C. Clarke Preface T

《Effective C++》:条款48:认识template元编程

Template metaprogramming(TMP,模板元编程)是编写template-based C++程序,编译的过程.template metaprogramming是用C++写的模板程序,编译器编译出具体化的过程.也就是说,TMP程序执行后,从templates具体化出来C++源码,不再是模板了. TMP有两个作用,一是它让某些事更容易.例如编写STL容器,使用模板,可是存放任何类型元素.二是将执行在运行期的某些工作转移到了编译期.还有一个结果是使用TMP的C++程序可能在其他方面

Effective C++ Item 48 认识 template 元编程

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:Template metaprogramming (TMP, 模板元编程)可将工作由运行期移往编译期,因而得以实现早期错误侦测和更高的执行效率 示例1: template<typename IterT, typename DistT> void advance(IterT &iter, DistT d){ if(typeid(typename std::iterator_t

《Effective C++》:条款48:理解力template 元编程

Template metaprogramming(TMP,模板元编程)这是写template-based C++规划.编译过程.template metaprogramming随着C++写模板程序,化的过程.也就是说,TMP程序运行后,从templates详细化出来C++源代码.不再是模板了. TMP有两个作用,一是它让某些事更easy. 比如编写STL容器,使用模板,可是存放不论什么类型元素.二是将运行在运行期的某些工作转移到了编译期.另一个结果是使用TMP的C++程序可能在其它方面更高效:较

C++ template —— template metaprogram(九)

metaprogramming含有“对一个程序进行编程”的意思.换句话说,编程系统将会执行我们所写的代码,来生成新的代码,而这些新代码才真正实现了我们所期望的功能.通常而言,metaprogramming这个概念意味着一种反射的特性:metaprogramminig组件只是程序的一部分,而且它也只生成一部分代码或者程序. 使用metaprogramming的目的是为了实现更多的功能,并且是花费的开销(代码大小,维护的开销等来衡量)更小.另一方面,metaprogramming的最大特点在于:某些

C++ template —— 表达式模板(十)

表达式模板解决的问题是:对于一个数值数组类,它需要为基于整个数组对象的数值操作提供支持,如对数组求和或放大: Array<double> x(1000), y(1000); ... x = 1.2 * x + x * y; 对效率要求苛刻的数值计算器,会要求上面的表达式以最高效的方式进行求值.想既高效又以这种比较紧凑的运算符写法来实现,表达式模板可以帮助我们实现这种需求. 谈到表达式模板,自然联想到前面的template metaprogramming.一方面:表达式模板有时依赖于深层的嵌套模

模板Trait 技术与简述template 元编程

模板Trait 技术 想了好久都没有想到合适的例子,已是干脆直接使用[1]中的例子就好了. STL 中引入了迭代器的概念.但是在本文中的例子不直接使用STL 的迭代器,而是写了一段很简短的代码,作为演示使用. 本例中的迭代器有三种: Forward_Iter,只能向前进,也就是只能加非负数 Bidirectional_Iter,可以双向增减 Random_Iter,可以随意增减 *本例并没有沿用STL 中的名称,请注意区别 对于三种的迭代器的偏移,大概如下: template<typename

Metaprogramming

Metaprogramming https://en.wikipedia.org/wiki/Metaprogramming 愿编程, 是一中编程技术, 制造的计算机程序,具有能力, 对待程序为他们的数据. 针对程语言的编程技术. Metaprogramming is a programming technique in which computer programs have the ability to treat programs as their data. It means that a

C++ Core Guidelines

C++ Core Guidelines September 9, 2015 Editors: Bjarne Stroustrup Herb Sutter This document is a very early draft. It is inkorrekt, incompleat, and pµøoorly formatted. Had it been an open source (code) project, this would have been release 0.6. Copy