Slashdot Mirror


Function Template Specialization in C++

friedo writes "About.com has an excellent two-part article (Part 1, Part 2) by Eric Nagler, author of "Learning C++," about "specializing" function templates in C++. "Rather than specifying an explicit type of all of the arguments or the return value in the definition of a function, placeholders are used. This reduces the need to create and maintain multiple copies of a function for different parameter types. But sometimes, it is not possible to write a single function template that works efficiently or even correctly for every argument type. It is in these cases that function template specialization is useful.""

22 of 89 comments (clear)

  1. Why is this here? by jbrandon · · Score: 3, Informative

    It's a very basic introduction to a very deep subject. Anyone who knows C++ knows the vast majority of this information.

    Plus, the navigation links on the second article are broken.

    For some really exciting C++, see www.gotw.ca or www.cuj.com

    Those sites have great articles about templates and overloading.

    1. Re:Why is this here? by devphil · · Score: 4, Interesting
      It's a very basic introduction to a very deep subject. Anyone who knows C++ knows the vast majority of this information.

      Good question. I don't know why.

      Frankly, if you want to see what templates and partial specialization can really do, go get Andrei Alexandrescu's book Modern C++ Design, from the In-Depth series (which I've posted on before, somewhere). As others have said before, the third chapter alone is worth the price of the book, just for what it will do for your understanding of specializations.

      I would post a link to Alexandrescu's site, but he's recently done something to it which causes mozilla to either crash instantly or hang indefinitely, requiring a kill -KILL in a console. Looks like either Java or frames.

      --
      You cannot apply a technological solution to a sociological problem. (Edwards' Law)
    2. Re:Why is this here? by Gilk180 · · Score: 2, Interesting

      It may be a very basic introduction, but the statement that "anyone" knows this information is probably not true.

      I would put template specialization in the same category as using a value as a template argument instead of a type ie. template. It is a feature that is very useful in some instances, but is rarely used even when useful and isn't taught in the majority of c++ classes.

    3. Re:Why is this here? by dthable · · Score: 2, Insightful

      A lot of people are in this boat of thinking of C++ as C with classes. Unless you really got into the last standard or had a need to use templates, STL, namespaces, etc. there was never a need to learn them.

      But hey, would you expect any less from /.?

    4. Re:Why is this here? by orthogonal · · Score: 5, Informative

      get Andrei Alexandrescu's book Modern C++ Design

      Agreed. A mind-blowing book, it will challenge your ideas about how to code.

      It's about more than just C++ templates; although its use of templates is revolutionary, the more general and important message of the book is about designing small, re-usable pieces of code that interact gracefully with each other (and to the extent possible, interact at compile-time rather than run-time). These ideas can, in theory, be applied to any langauge that supports class typing; in practice I've used in in pre-generics Java to a small (and non-revolutionary) extent.

      If you program in any significant way in C++, you owe it to yourself to get the book, if for no other reason than to re-experience that sense of wonder you felt when you compiled your first program.

  2. Re:Parametric polymophism. by kruntiform · · Score: 2, Funny

    If I may troll just a little, the first one is better because most programmers are afraid of anything that doesn't look like C and because they like to type in lots of boilerplate code because it makes them feel productive.

  3. Re:Horrible example by renehollan · · Score: 3, Insightful
    This is a perfect example of how NOT to use the expressive power of C++.

    Oh, I wholeheartedly disagree!

    Any junior programmer I encountered who was interested in generic programming would get my greatest support, and gentle guidance. I might not permit his initial attempts in production code, but I wouldn't stifle his interest either. How the hell else is a programmer supposed to mature their art?

    Yes, it's real easy to fuck yourself up a million ways from Sunday, and none of them good, when you try to exploit a language mechanism that provides a two level language, with selective binding, but the power is immense. Just look at the use of C++ template metaprogramming for compile-time optimization -- it solves the cost of abstraction problem.

    The syntax sucks, of course, and that makes it hard to comprehend, but that's to be expected when the Turing completeness of a language facility comes not by design, but by accident.

    Ideally, in any project architects define the desirable coding abstractions, senior programmers implement the support for them, and more junior programmers code using them -- there is no more need for a junior programmer to understand template arcana than there is for them to understand the building of glibc (which is a either a horrid abuse of make, or a beautiful hack, depending on your point of view -- xargs is your friend).

    When you strive to reducing all the code your team writes to that understandable by the lowest common denominator of skill available, you compress the scale of productivity exhibited by your team members greatly. Given that coding productivities can vary by an order of magnitude within a team (some have said, this really isn't something you want to do.

    Instead you should build layers of abstraction, including meta languages within your code, giving the difficult implementations to the senior members, and the simpler ones (considering also the abstractions you've built) to more junior ones.

    Part of the problem with your complaint is that you continue to see pointers to NUL-terminated character arrays instead of strings, right down to your pseudo-Hungarian notation. The whole idea is not to compare pointers, but strings, with the pointer implementation of a handle to the first character of the string a string implementation detail, ideally hidden.

    --
    You could've hired me.
  4. Re:Parametric polymophism. by renehollan · · Score: 2, Informative
    Why should I care if the language makes it an object or a function as long as it does what I want when I call it?

    If a function is a first class object, then you can (a) queue it for later execution, (b) manage an ordered list of functions for execution, (c) partially qualify the parameters for later execution, with the remainder specified at call (i.e. dispatch) time. Read up on functors some time.

    --
    You could've hired me.
  5. Re:Hi fallutin' OO zealots!!! by renehollan · · Score: 2, Informative
    Look folks, it's called a function pointer. We've had those in "C" for a long long time, so don't act like you invented it.

    A functor is far more than a "function pointer".

    A functor lets you fix none, all, or some of the parameters that are to be passed to the function via the pointer to it. This has practial applications. For example, an editor with an "undo" function that dispatches based on queued functors of edit operations, can also implement the "undo" by mapping that queue of functors to a nre queue of functors that do the "undo"ing. Try doing that with a function pointer.

    Also, because one has the opportunity to bind the function to it's parameters at different times, they don't all have to be known at the same time.

    I've put functors to good use when dispatching encapsulated function calls with queues of functions to call when those calls result in later callbacks in a multithreaded system -- there being a relationship in the dispatched functors and the callback parameters. You really should read Modern C++ Design to develop an appreciation for these techniques. They aren't obvious at first.

    --
    You could've hired me.
  6. It just gets even more convoluted by Viol8 · · Score: 3, Insightful

    The problem with C++ and swith this concept , is that it makes things quicker for the intial programmer but a DAMN site harder for a
    maintenance programmer to understand.

    If I see:
    A var1;
    B var2;
    var3 = var1 + var2;

    in the middle of a C++ program I have NO idea what the hell is going on unless I trawl through reams of
    definitions to find out what the templates were set to and how the operator was overloaded. Sorry , but this isn't progress , its obfuscation.
    Give me more lines of duplicate code with obvious definitions instead of templates and overloads everywhere.

    1. Re:It just gets even more convoluted by Viol8 · · Score: 2, Insightful

      Actually I was thinking more:

      class foobar var1;
      class wibble var2;

      var3 = concatenateNames(&var1, &var2);

      Or something like that. If people only ever used operators for their namesake operations there wouldn't be a problem but how often have you seen
      code where + is overloaded to do all sorts of different operations depending on the STATE of what gets passed to it, never mind
      the actual types that get passed.

      "operator overloading is just syntactic sugar for a function call: it's no more and no less obfuscated"

      That may be true , but using your example its a damn site easier to grep through some source code for "add(" and get maeningfull output than for
      the + operator!

    2. Re:It just gets even more convoluted by renehollan · · Score: 2, Insightful
      I have NO idea what the hell is going on unless I trawl through reams of definitions to find out what the templates were set to and how the operator was overloaded

      ...unless you understand the types A and B, and the meaning of operator+(const A&, const B&). If A and B represented arbitrary precision numbers, it would be perfectly natural to use operator+ for their addition.

      The counter to your objection is that if you don't understand the types in a program, you've got no business messing with objects of those types. That is not an argument against user-defined objects or operator overloading, but one for proper architecture with no extraneous types or templates.

      Basically, these mechanisms make it easier to use the implementation language to express code architecture and design pattern models -- programming at a higer level of abstraction if you will, but programming nevertheless. The trouble is that many so-called "programmers" lack the skill to do this and get miffed.

      --
      You could've hired me.
    3. Re:It just gets even more convoluted by Mr.+Slippery · · Score: 2, Insightful
      Seriously, operator overloading is just syntactic sugar for a function call: it's no more and no less obfuscated.

      True, but in my experience operator overloading is more subject to abuse. With a function, the devloper has to give it a name, and the potential namespace is much larger than the operator namespace.

      --
      Tom Swiss | the infamous tms | my blog
      You cannot wash away blood with blood
  7. Re:why is this newsworthy ? by wandazulu · · Score: 2, Informative

    Unless you've done the majority of your C++ programming using VC4.1/4.2/5/6 which doesn't support this feature, but all of a sudden 7.1 does and you want to see what you've been missing.

  8. Upcoming "Developers" Headlines: by avdi · · Score: 2, Funny

    Variable Assigment in Java
    Using Perl Regular Expressions to Process text
    Python Operator Overloading
    Using Lisp Macros

    --

    --
    CPAN rules. - Guido van Rossum
  9. Re:Horrible example by renehollan · · Score: 2, Insightful
    Indeed. The pointer to NUL-terminated sequence of characters is sooo idiomatically a string from the "C days", that it's probably more understandable to preserve that idiom than not. Heck, that's the only way to implement string literals without the use of an object, and we know how much we love our string literals.

    I suppose if I were to implement the template, I'd have a greater member function that throws an exception when passed a pointer, with a partial specialization that works for pointers to chars (unsigned chars, const chars, const unsigned chars). The only grief with this approach is that you can't partially specialize non-member function templates and must resort to a class.

    --
    You could've hired me.
  10. Re:Parametric polymophism. by renehollan · · Score: 2, Insightful
    But partial application and closures are a extremely painful in C++.

    Not surprising, since the two-level nature of C++, via templates is more of an accident rather than a design. There are better languages out there that support these facilities in a clean fashion.

    Nevertheless, these are extremely useful and poweful techniques. Every language has it's "dark and ugly" side -- think about the macro hackery you need to support parallel arrays in C.

    --
    You could've hired me.
  11. Another marginal C++ feature by Animats · · Score: 3, Insightful
    There's a group of C++ programmers who are into abusing the template mechanism into a compile-time programming environment. This is not a good thing.

    The most impressive perversion of this ilk is the Blitz numeric library. This does loop unrolling by using the template library as a term-rewriting system. The resulting code is very complex, hard to debug, and of marginal value. Blindly unrollilng loops without understanding the target architecture at the instruction level is a lose on many modern superscalar machines. Newer machines tend to do loops faster than straight-line code.

    Debugging compile-time processing in C++ is tough. You can't step through the process, you can't print anything, and the compiler doesn't provides any output about what's going on. Programming at compile time has a long, sordid history (LISP macros come to mind), and C++ is an inferior environment for it anyway.

    If you're using a compiler which will develop templates in-line, decide if-statements at compile time, and discard unreachable code (which includes most modern compilers), it's better to write code which works that way, rather than use template specialization.

  12. I mostly disagree by devphil · · Score: 2, Interesting
    There's a group of C++ programmers who are into abusing the template mechanism into a compile-time programming environment. This is not a good thing.

    In your opinion. But this "group" is steadily growing larger and larger, and the number of critics are growing smaller, as they see the expressive power of templates.

    One of the standard axioms of programming is that you know you have created a useful tool when others begin successfully using it for purposes other than that which was originally intended. Some people refuse to see templates as anything other than ways to write yet another typesafe-container-of-T. Others push a little farther into families of functions. Still others, like Alexandrescu and Veldhuizen and the entire Boost membership, have pushed on into a very different design. Automatic generation and maintenance of entire class hierarchies, to give a single example, is a big, big win.

    Fine, you don't like it. Fair enough. I don't think anybody is trying to sell it as a silver bullet. But the expressive power of compile-time programming has been aptly demonstrated, and it's going to be around for a while. C++98 has some difficulties with it, because most of the techniques were discovered late in the game, almost by accident. Current proposals for C++0x contain a number of tweaks and extensions to make it easier.

    Blindly unrollilng loops without understanding the target architecture at the instruction level is a lose on many modern superscalar machines. Newer machines tend to do loops faster than straight-line code.

    This assertion is what made me respond. It's like saying, "All C programs suck," or something equally nebulous. Some machines do some loops faster than straight code under some conditions. But you won't see loop unrolling being disabled by default anytime soon, on any architecture. That decision has to be based on measurements, and testing, and experimentation, and hey- just the same thing you go through when making design decisions like whether to write C++ or assembly, and which kind of C++, and how to decompose the problem, etc, etc.

    Sometimes these techniques will be appropriate, and sometimes they won't. Don't blindly condemn them all. Take a look at some of the examples where the template code produces the same assembly as the done-by-hand code.

    If you're using a compiler which will develop templates in-line, decide if-statements at compile time, and discard unreachable code (which includes most modern compilers), it's better to write code which works that way, rather than use template specialization.

    (Until, of course, you need to do it all with more than one type at a time, then it's back to copy-and-pasting everywhere.)

    You assert that it's "better" to do it your way. Do you have numbers to back this up, or is it just your opinion? (It's okay if it's an opinion, I was just curious and wanted to check.)

    --
    You cannot apply a technological solution to a sociological problem. (Edwards' Law)
    1. Re:I mostly disagree by voodoo1man · · Score: 2, Informative
      In LISP, macros became so bloated that Scheme had to be invented to strip the language down. But it was too late.
      Before flaming, it helps to actually check who, what and why invented Scheme.

      Sussman and Steele's compiler was first of all an implementation of Hewitt's ACTORS, and second of all an interpreter that re-wrote Scheme in CPS style (along the way inventing general tail-recursion) targeted at Maclisp. The only "bloat" that Scheme fixed was the "funarg problem" of early lisps by introducing full lexical scoping (along the way inventing the closure) and applicative order reduction/evaluation for function arguments, a convention that most later Lisps adopted (those that did not reportedly disappeared without a trace in the 1980s). The original Scheme did not include macros, and didn't have FEXPRS (ie - all functions evaluated their arguments, as mentioned above), but it did have quote, which is enough to build macros. I'm pretty sure they used some form of macros for transforming special forms to lambdas (off the top of my head, I think Steele mentions dirty macros in Compiler Optimization based on viewing LAMBDA as rename plus GOTO in Winston, ed, Artificial Intelligence - An MIT Perspective, Vol. 2). I know AI Memo 349 (Sussman, Steele, SCHEME an interpteter for extended lambda calculus) mentions something about AMACROs in the implementation.

      But certainly neither of them disparaged macros (as a matter of fact, I do recall a not too old discussion on the Lighweight Languages mailing list where Steele defends macros against an attack using the same (lack of) arguments as yours).

      PS - yes, it certainly was too late for Scheme, but luckily they added syntax-case back in somewhere around R4RS. Better late than never.

      --

      In the great CONS chain of life, you can either be the CAR or be in the CDR.

  13. But obvious (or not) to whom? by Anonymous+Brave+Guy · · Score: 2, Insightful
    You really should read Modern C++ Design to develop an appreciation for these techniques. They aren't obvious at first.

    In fairness, while MC++D is indeed an interesting book for the serious C++ hacker, one should bear in mind (as the original post to this subthread pointed out) that most of the clever function stuff in it is second nature in a functional language, wherein it is obvious and a typical programmer will achieve much the same goals with far less obfuscation and much more concise code.

    I've noticed that a lot of the "leading edge" C++ tools, from the STL to Loki and Boost, are pretty much trying to implement programming tools from other languages that have demonstrated value -- higher order functions, automatic resource management, high performance mathematics, complex data structures, regular expressions -- within C++. There's nothing wrong with that, of course, but the underlying techniques aren't particularly clever if you're familiar with languages that specialise in them.

    Every day, as I work on a complex mathematical library written in C++, I internally groan at writing five times as many lines of code as I'd need in any major functional language because C++ has such weak support for a couple of key areas that we use all the time. The practicalities of our situation may make it the most appropriate tool we've found for the job, but looking at templates, macro hackery or cute inheritance graphs doesn't stop me wishing I could use higher order functions when working with complex mathematics...

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
  14. Re:Is this even correct C++? by renehollan · · Score: 2, Insightful
    Again, there is no partial specialization what so ever in his examples. Partial specialization involves pointers and references to unknown types. Of which there is none in the examples. Partial specialization does not mean part of full specialization.

    WQe are not talking about the same thing. Consider the class template:

    template <typename T, typename U> class X { ... };

    This can be partially specialised thus:

    template <typename U> class X<int, U> { ... };

    and fully thus:

    class X<int, long> { ... };

    or template <> class X<int, long> { ... };

    though, technically the last example above is an explicit specialization, whereas the one above it is a full specialization.

    You're talking about cases like

    template <typename T> class X<T, T&> { ... }

    which are also partial template specializations.

    But, I reiterate, you can't partially specialize member functions or non-member functions in the same way -- you can only totally specialize member functions and use overloading for non-member functions to similar effect. I recall that the syntax for non-member functions allows a fully-specialized template syntax to be used (instead of simple signature overloading), but I may be mistaken there.

    So,

    template <typename T, typename U> void f(T t, U u) { ... }

    void f(int t, long u)<int, long> { ... }

    as opposed to simply

    void f (int t, long u) { ... }

    The first would be a full specialization and the latter a simple overload. Something in my mind tells me that the full specialization function syntax is, in fact, legal, though I don't have the time to try it now.

    See Alaxandrescu, Andrei, "Modern C++ Design", Addison-Wesley, New York, 2001, pp. 26-28.

    --
    You could've hired me.