《
C++ Template Metaprogramming》的第四章提到了缓式评估(lazy evaluation)和短路行为(short-circuit behavior)两个概念,我编写了一个测试这两种行为的程序,但无法通过编译,还以为是编译器的问题,后来经过仔细思考,才发现是自己弄错了。
我最初的理解是,缓式评估(
lazy evaluation)就是if_<>在看到第一个参数为true时,就不会去碰第三个参数;同样,短路行为(short-circuit behavior)就是or_<>在看到第一个参数为true时,也不会去碰第二个参数。于是,我写了以下测试代码:
#include <iostream> #include <boost/mpl/or.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/identity.hpp> #include <boost/type_traits/is_reference.hpp> namespace mpl = boost::mpl; template <typename T> struct LazyEvaluationTest : mpl::if_< typename boost::is_reference<T> , typename mpl::identity<T>, mpl::identity<T&> >::type { }; template <typename T> struct ShortCircuitTest : mpl::or_<boost::is_reference<T>, boost::is_reference<T&> > { }; int main() { typedef LazyEvaluationTest<int&>::type t1; std::cout << ShortCircuitTest<int&>::value << std::endl; return 0; } |
我以为,虽然两个测试用的元函数都是以
int&来实例化的,但是相应的if_<>和or_<>里的第一个参数都是true,所以应该不会触碰到后面关于T&的模板。结果编译器报出了“reference to reference”的错误。
一开始,我还以为是
GCC编译器的问题。后来仔细一想,缓式评估(lazy evaluation)和短路行为(short-circuit behavior)的正确含义并不是象我前面所理解的那样。正确的理解应该是:if_<>在看到第一个参数为true时,也要去检查第三个参数的声明是否有效,但不会去触碰它里面的东西(如::type或::value),同样or_<>在看到第一个参数为true时,也要检查第二个参数的声明,但也不会去触碰嵌套在里面的东西,除非嵌套的东西被显式调用。
还是以《
C++ Template Metaprogramming》一书中的例子为例,如果你这样写:
template <class T> struct param_type : mpl::if_< typename boost::is_scalar<T>::type , T , typename boost::add_reference<T const>::type > { }; |
那么不管
boost::is_scalar<T>::type是true还是false,编译器都要实例化boost::add_reference<T const>以获得嵌套在其内部的::type。而如果你这样写:
template <class T> struct param_type : mpl::if_< // forwarding to selected transformation typename boost::is_scalar<T>::type , mpl::identity<T> , boost::add_reference<T const> >::type {
};
|
则无论
boost::is_scalar<T>::type是true还是false,编译器都会检查boost::add_reference<T const>的声明是否合法,但不会实例化它,直至你需要嵌套在其内部的::type。
理解了这些之后,我修改了测试的代码,如下:
#include <iostream> #include <boost/mpl/or.hpp> #include <boost/mpl/if.hpp> #include <boost/mpl/identity.hpp> #include <boost/type_traits/is_reference.hpp> namespace mpl = boost::mpl; template <typename T> struct ErrorTest : boost::is_reference<T&> { }; template <typename T> struct LazyEvaluationTest : mpl::if_< typename boost::is_reference<T> , mpl::identity<T>, ErrorTest<T> >::type { }; template <typename T> struct ShortCircuitTest : mpl::or_<boost::is_reference<T>, ErrorTest<T> > { }; int main() { typedef LazyEvaluationTest<int&>::type t1; std::cout << ShortCircuitTest<int&>::value << std::endl; return 0; } |
修改后的代码中增加了一个
ErrorTest<T>模板,它继承自boost::is_reference<T&>,如果使用引用类型(如int&)来实例化,就会产生“reference to reference”的编译错误。接着,两个测试用的元函数LazyEvaluationTest与ShortCircuitTest分别继承自if_<>和or_<>,并且if_<>和or_<>均带有参数ErrorTest<T>,但由于在使用引用类型(如int&)对LazyEvaluationTest或ShortCircuitTest进行实例化时,if_<>和or_<>的第一参数boost::is_reference<T>将为true,因此编译器只会检查ErrorTest<T>的声明而不会触碰其内部(包括它的基类),所以这次不会再出现编译错误了。