《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>的声明而不会触碰其内部(包括它的基类),所以这次不会再出现编译错误了。
发表于 @ 2006年12月07日 14:58:00|评论(loading...)|编辑