开发者

Template meta-magic

I recently came across a kind of dispatch pattern based on the "concepts" of the data types (I think that's the correct use of the term concepts, if not, attributes??).

It seems to me to be a bit more involved than the type of thing I've seen before, and before optimisation at least, introduces temporary variables and function calls. My question is (1) does it all actually get optimised away?? (2) is this the "best" way of doing this kind of type based dispatch??

The code I was reading dealt with iterator types, so I'll stick with that as an example of the pattern. The function iter_kind below takes a variable of any type and returns a dummy variable of a particular "concept" type. Something like random_access_iterator_kind, or if _Iter was a non-iterator type null_iterator_kind.

template <typename _Iter>
    INLINE_CALL typename iterator_traits<_Iter>::iter_kind iter_kind(_Iter&)
{
    typename iterator_traits<_Iter>::iter_kind _ret;
    return ( _ret );
}

It works using the usual meta-magic via specialisation of the iterator_traits class. I'm happy with all that stuff.

iter_kind gets used to delegate to different implementations of a particular function, as an example:

template &l开发者_Python百科t;typename _Iter, typename _Pred>
    _Iter binary_search(_Iter head, _Iter tail, _Pred pred_less)
    {
        binary_search_impl(head, tail, pred_less, iter_kind(head));
    }

template <typename _Iter, typename _Pred>
    _Iter binary_search_impl(_Iter head, _Iter tail, _Pred pred_less,
                             random_access_iterator_kind)
    {  // actual implementation...
    }

template <typename _Iter, typename _Pred>
    _Iter binary_search_impl(_Iter head, _Iter tail, _Pred pred_less,
                             bidirectional_iterator_kind)
    {
        assert(false);  // can't do bin search without random access iters!!
    }


Yes, this stuff really does all get optimized away. Compilers are written specifically to get rid of this gunk after it's done, because it's a common trick. Functions and overloading this way are done because, well, it's simply a lot easier than doing it any other way. This is a common trick used in the implementation of the Standard library.


Well, no, it's not the best way. You can select the correct overload at compile time like this, for example:

 template <typename _Iter, typename _Pred>    
   _Iter binary_search(_Iter head, _Iter tail, _Pred pred_less)    
   {        
     binary_search_impl(head, tail, pred_less,
      std::iterator_traits<_Iter>::iterator_category());    
   }

Even better way is to use SFINAE in the signature of the actual function, with zero overhead at run-time: basically you'd use std::enable_if on either iterator category to tell the complier which overload to pick.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜