随着Template的大量使用,我们陷入了这样的一个境地.虽然有编译时期的检测,但是往往面对编译错误,我们却无从下手.特别是当你面对不是很熟悉的"组件"的时候.
std::list<std::string> coll;
// Find the first element greater than "A"
std::list<std::string>::iterator pos;
pos = std::find_if(coll.begin(),coll.end(),std::bind2nd(std::greater<int>(),"A")); // criterion
这个是<<C++ Templates: The Complete Guide>>中的一个例子,下面给出VC7.1的编译错误信息:
e:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\functional(334) : error C2440: “初始化” : 无法从“const char [2]”转换为“std::binary_function<_Arg1,_Arg2,_Result>::second_argument_type”
with
[
_Arg1=int,
_Arg2=int,
_Result=bool
]
该转换要求 reinterpret_cast、C 样式转换或函数类型转换
c:\Documents and Settings\sevecol\My Documents\Visual Studio Projects\Game\Game.cpp(37) : 参见对正在编译的函数模板实例化“std::binder2nd<_Fn2> std::bind2nd<std::greater<_Ty>,const char[2]>(const _Fn2 &,const char (&))”的引用
with
[
_Fn2=std::greater<int>,
_Ty=int
]
e:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\include\algorithm(64) : error C2664: “std::binder2nd<_Fn2>::result_type std::binder2nd<_Fn2>::operator ()(std::binder2nd<_Fn2>::argument_type &) const” : 不能将参数 1 从“std::allocator<_Ty>::value_type”转换为“std::binder2nd<_Fn2>::argument_type &”
with
[
_Fn2=std::greater<int>
]
and
[
_Ty=std::string
]
and
[
_Fn2=std::greater<int>
]
不是对“const”的引用不能绑定到非 lvalue
c:\Documents and Settings\sevecol\My Documents\Visual Studio Projects\Game\Game.cpp(37) : 参见对正在编译的函数模板实例化“_InIt std::find_if<std::list<_Ty>::iterator,std::binder2nd<_Fn2>>(_InIt,_InIt,_Pr)”的引用
with
[
_InIt=std::list<std::string>::iterator,
_Ty=std::string,
_Fn2=std::greater<int>,
_Pr=std::binder2nd<std::greater<int>>
]
这段错误信息比<<C++ Templates: The Complete Guide>>(出版于November 12, 2002)使用GNU C++给出的错误信息(见附录)要清晰多了,但要看明白怎么回事,也要花不少工夫,这个时候如果有杯又香又浓的雀巢咖啡,啊,真是味道好极了!
面对这种信息膨胀,我们该怎么做?
Concept
A concept is a set of requirements (valid expressions, associated types, semantic invariants, complexity guarantees, and so on) that a type must fulfill to be correctly used as an argument in a call to a generic algorithm. However, C++ has no explicit mechanism for representing concepts — template parameters are merely place holders and express no constraints on the template argument. By convention, template parameters are given names corresponding to the concept that is required, but C++ compilers do not enforce compliance to the concept at the point where the template parameter is bound to an actual type.
Naturally, a compile-time error occurs if a generic algorithm is invoked with a type that does not fulfill at least the syntactic requirements of the concept. However, this error will not reflect that the type did not meet all requirements of the concept per se. Rather, the error may occur at a point where an expression is not valid for the type, or where a presumed associated type is not available. The resulting error messages are largely uninformative and basically impenetrable.
What is required is a mechanism for enforcing "concept safety" at (or close to) the point of instantiation. The Boost Concept Checking Library (BCCL) uses Standard C++ constructs to enforce early concept compliance and provide more informative error messages upon non-compliance. The techniques described here only address the syntactic requirements of concepts (the valid expressions and associated types). These techniques do not address the semantic invariants or complexity guarantees that are also part of concept requirements (although this is an active topic in our research group).
(C++ Concept Checking,Dr. Dobb''s Journal June 2001,A better practice for template programming -By Jeremy Siek and Andrew Lumsdaine)
也就是说是问题自己暴露出来,我们用一个机制去检测模版类型参数是否满足我们的需要.
Concept Checking Classes
To check a concept, we employ a special-purpose class (a concept checking class) that ensures that a given type (or set of types) models the given concept. An example of a concept checking class from the BCCL is the EqualityComparableConcept class that corresponds to the EqualityComparable requirements described in 20.1.1 of the C++ Standard, and to the EqualityComparable concept documented in the SGI STL.
template <class T>
struct EqualityComparableConcept;
The template argument T represents the type to be checked. That is, the purpose of EqualityComparableConcept is to make sure that the template argument given for T models the EqualityComparable concept. Each concept checking class has a member function named constraints(), which contains the valid expressions for the concept. To check whether some type is EqualityComparable, we need to instantiate the concept checking class with the type, then find a way to get the compiler to compile the constraints() function without actually executing the function. The BCCL defines two utilities that make this easy: function_requires() and BOOST_CLASS_REQUIRES. The function_requires() function can be used in function bodies and the BOOST_CLASS_REQUIRES macro can be used inside class bodies.
The function_requires() function takes no arguments, but has a template parameter for the concept checking class. This means that the instantiated concept checking class must be given as an explicit template argument; see Listing One(a). With this concept check inserted, if the class foo is not a model of EqualityComparable (it has not defined operators == and !=), then function_requires() will catch the error upfront, instead of allowing the error to occur somewhere inside the template function.
(C++ Concept Checking,Dr. Dobb''s Journal June 2001,A better practice for template programming -By Jeremy Siek and Andrew Lumsdaine)
下面来看一个具体的例子:
template <class T>
void some_function_template(T x)
{
function_requires< EqualityComparableConcept<T> >();
// ...
};
// In the user''s code:
class foo {
//...
};
int main() {
foo f;
some_function_template(f);
return 0;
}
这样如果类型不满足函数的需要,就会在函数的开头产生错误信息.这个错误信息要比我们使用类型的时候产生的要友好的多.当然这不是最完美的解决方法.
附录:
<<C++ Templates: The Complete Guide>>(出版于November 12, 2002)使用GNU C++给出的错误信息
/local/include/stl/_algo.h: In function ''struct _STL::_List_iterator<_STL::basic
_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::_Nonconst_tra
its<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > >
_STL::find_if<_STL::_List_iterator<_STL::basic_string<char,_STL::char_traits<cha
r>,_STL::allocator<char> >,_STL::_Nonconst_traits<_STL::basic_string<char,_STL::
char_traits<char>,_STL::allocator<char> > > >, _STL::binder2nd<_STL::greater<int
> > >(_STL::_List_iterator<_STL::basic_string<char,_STL::char_traits<char>,_STL:
:allocator<char> >,_STL::_Nonconst_traits<_STL::basic_string<char,_STL::char_tra
its<char>,_STL::allocator<char> > > >, _STL::_List_iterator<_STL::basic_string<c
har,_STL::char_traits<char>,_STL::allocator<char> >,_STL::_Nonconst_traits<_STL:
:basic_string<char,_STL::char_traits<char>,_STL::allocator<char> > > >, _STL::bi
nder2nd<_STL::greater<int> >, _STL::input_iterator_tag)'':
/local/include/stl/_algo.h:115: instantiated from ''_STL::find_if<_STL::_List_i
terator<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,
_STL::_Nonconst_traits<_STL::basic_string<char,_STL::char_traits<char>,_STL::all
ocator<char> > > >, _STL::binder2nd<_STL::greater<int> > >(_STL::_List_iterator<
_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<char> >,_STL::_N
onconst_traits<_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<c
har> > > >, _STL::_List_iterator<_STL::basic_string<char,_STL::char_traits<char>
,_STL::allocator<char> >,_STL::_Nonconst_traits<_STL::basic_string<char,_STL::ch
ar_traits<char>,_STL::allocator<char> > > >, _STL::binder2nd<_STL::greater<int>
>)''
testprog.cpp:18: instantiated from here
/local/include/stl/_algo.h:78: no match for call to ''(_STL::binder2nd<_STL::grea
ter<int> >) (_STL::basic_string<char,_STL::char_traits<char>,_STL::allocator<cha
r> > &)''
/local/include/stl/_function.h:261: candidates are: bool _STL::binder2nd<_STL::g
reater<int> >::operator ()(const int &) const

浙公网安备 33010602011771号