Skip to main content

Templates

  • Chapter
  • 2811 Accesses

Abstract

Programming is the process of teaching something to a computer by talking to the machine in one of its common languages. The closer to the machine idiom you go, the less natural the words become.

This is a preview of subscription content, log in via an institution.

Buying options

Chapter
USD   29.95
Price excludes VAT (USA)
  • Available as PDF
  • Read on any device
  • Instant download
  • Own it forever
eBook
USD   69.99
Price excludes VAT (USA)
  • Available as EPUB and PDF
  • Read on any device
  • Instant download
  • Own it forever
Softcover Book
USD   89.99
Price excludes VAT (USA)
  • Compact, lightweight edition
  • Dispatched in 3 to 5 business days
  • Free shipping worldwide - see info

Tax calculation will be finalised at checkout

Purchases are for personal use only

Learn about institutional subscriptions

Notes

  1. 1.

    Loosely speaking, that’s the reason for the “meta” prefix in “metaprogramming”.

  2. 2.

    In modern C++ there are more, but you can consider them extensions; the ones described here are metaprogramming first-class citizens. Chapter 12 has more details.

  3. 3.

    Usually any integer type is accepted, including named/anonymous enum, bool, typedefs (like ptrdiff_t and size_t), and even compiler-specific types (for example, __int64 in MSVC). Pointers to member/global functions are allowed with no restriction; a pointer to a variable (having external linkage) is legal, but it cannot be dereferenced at compile time, so this has very limited use in practice. See Chapter 11.

  4. 4.

    The linker may eventually collapse them, as they will likely produce identical machine code, but from a language perspective they are different.

  5. 5.

    An exception being that literal 0 may not be a valid pointer.

  6. 6.

    See Sections 1.3.6 and 11.2.2 for more complete discussions.

  7. 7.

    See the note in Section 1.3.2.

  8. 8.

    You can cast a floating-point literal to integer, so strictly speaking, (int)(1.2) is allowed. Not all compilers are rigorous in regard to this rule.

  9. 9.

    The use of __LINE__ as a parameter in practice occurs rarely; it’s popular in automatic type enumerations (see Section 7.6) and in some implementation of custom assertions.

  10. 10.

    We have to choose a different name, to avoid shadowing the outer template parameter scalar_t.

  11. 11.

    See also http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#666.

  12. 12.

    Even if it’s not a correct example, an open-minded reader may want to consider the relationship between std::string, std::wstring, and std::basic_string<T>.

  13. 13.

    See 1.4.9.

  14. 14.

    As a side note, this shows once more that in TMP, the less code you write, the better.

  15. 15.

    Compare with the use of typename described in Section1.1.1.

  16. 16.

    See the “brittle base class problem” mentioned by Bjarne Stroustrup in his “C++ Style and Technique FAQ” at http://www.research.att.com/∼bs/.

  17. 17.

    The exact rules are documented and explained in [2]. You’re invited to refer to this book for a detailed explanation of what’s summarized here in a few paragraphs.

  18. 18.

    See the next section.

  19. 19.

    This example is taken from [2].

  20. 20.

    In particular, the compiler is not required to notice that void f(arg<2*N>) and void f(arg<N+N>) are the same template function, and such a double definition would make a program ill-formed. In practice, however, most compilers will recognize an ambiguity and emit an appropriate error.

  21. 21.

    Template functions cannot be partially specialized, but only overloaded.

  22. 22.

    Unfortunately, some popular compilers tolerate this.

  23. 23.

    Consider the simpler case when outer<T> is a container, inner1 is an “iterator,” inner2 is “const_iterator,” and they both derive from an external common base, basic_outer_iterator.

  24. 24.

    The extra “Y” is little more than poetic license. Refer to the excellent article from Danny Kalev at http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=454.

  25. 25.

    Except std::numeric_limits<T>::quiet_NaN().

  26. 26.

    Even source code has a lifecycle and eventually it’s going to “die,” i.e., it will be rewritten from scratch. However the more robust the design, the longer its life will be, and style is part of the design. See also [5].

  27. 27.

    As discussed in [5], usually member function names should be actions. Thus empty should be a synonym for make_empty and not for is_empty. However, STL convention is established and universally understood. When in doubt, do as std::vector does.

  28. 28.

    See http://en.cppreference.com/w/cpp/language/constexpr for the exact requirements and specifications.

  29. 29.

    The difference between the last two implementations is largely how they react to an invalid syntax. As an exercise, consider some malicious code like MXT_SORT2(x, y) if (true) throw an_exception;.

  30. 30.

    The reader might want to review the simple example early in this chapter.

  31. 31.

    See Section 6.2.1.

  32. 32.

    Some compilers, such as MSVC71, used to have problems with unnamed parameters; refer to paragraph 11.3.3 for a detailed example.

  33. 33.

    Some authors reserve the keyword typename for this purpose. In other words, they declare template <typename T> to mean that T is “any type” and template <class T> to suggest that T is indeed a class as opposed to a native type. However, this distinction is rather artificial.

  34. 34.

    Note that this is not a formal requirement; it’s just a name! The name reflects how we think the type should be; later we will enforce this, if necessary.

  35. 35.

    The mathematically inclined reader should consider the latter as a special case of the former. The constant 5’ can be replaced by a type named five or static_value<int, 5>. This leads to greater generality. See [3] for more information.

  36. 36.

    It should reside in an anonymous namespace, but this does not make it inaccessible.

  37. 37.

    The advantages are described extensively in Apple Technical Note TN2185; refer to the following page: http://developer.apple.com/technotes/tn2007/tn2185.html.

  38. 38.

    Such functions are denoted shims in [5].

  39. 39.

    If a is an array of T of length 2, then (char*)(&a[1])-(char*)(&a[0]) is a ptrdiff_t, which is at least as large as sizeof(T). That means ptrdiff_t is at least as large as int as well. This argument actually shows that every result of sizeof can be stored in a ptrdiff_t. A generic size_t may not be stored in a ptrdiff_t, because sizeof is not necessarily surjective—there may be a size_t value that is larger than every possible sizeof.

  40. 40.

    For example, to create a copy of std::string takes time proportional to the length of the string itself, so this depends not only on the type, but also on the instance; alternatively, copying a double is a constant-time operation. Mathematically speaking, the notion of “constant time” is not well defined in C++; the issue is too complex for a footnote, but we’ll sketch the idea. An algorithm is O(1) if its execution time is bounded by a constant K, for any possible input. If the number of possible inputs is finite, even if it’s huge, the algorithm is automatically O(1). For example, in C++ the sum of two int is O(1). In general, the C++ memory model has a finite addressable space (because all objects have a fixed size, and an “address” is an object) and this implies that the number of possible inputs to some algorithms is finite. Quicksort complexity is O(N*log(N)), but std::sort may be formally considered O(1), where—loosely speaking—the constant K is the time required to sort the largest possible array.

  41. 41.

    Compare with Section 2.3.1.

  42. 42.

    Some objects may want to check in advance if overwrite is feasible. For example, if T is std::string whose size()==that.size() then it might be able to perform a safe memcpy.

  43. 43.

    Note that the best option is to demand that the paired objects provide suitable operators, so we delegate the comparison. For example, pair<const char*, int> and pair<std::string, int> are unlikely to trigger the construction of temporary strings, because we expect the STL to supply an operator==(const char*, const std::string&).

  44. 44.

    The definition of “aggregate” changed in C++11, where uniform initialization was introduced. As the issue is quite complex and detailed, readers may want to see the bibliography.

  45. 45.

    There may be a huge cost in increased complexity that comes from writing code “totally bulletproof ”. Sometimes this complexity will also inhibit some compiler optimizations. As a rule, programmers should always reason pragmatically and accept the fact that code will not handle every possible corner case.

  46. 46.

    See Section 1.6.

  47. 47.

    By platform, usually we mean the set { processor, operating system, compiler, linker }.

  48. 48.

    From empirical analysis, it looks like sometimes a protected empty destructor inhibits optimizations. Some ­measurements have been published in [3].

  49. 49.

    Note that recursive<T, -1> will not compile.

  50. 50.

    Most compilers implement this optimization, at least in the case of single inheritance.

  51. 51.

    Mac OS X 10.4.8, XCode 2.4.1, GCC 4.01.

  52. 52.

    Set warnings at maximum level only once, in the very last development phase or when hunting for mysterious bugs.

  53. 53.

    See 3.7.2 in the standard.

  54. 54.

    Including the author of this book.

  55. 55.

    Of course, there are known exceptions to this rule: some C runtime functions (sprintf, floor) and even a few STL functions (string::operator+).

  56. 56.

    Releasing memory may be a totally different matter, anyway.

  57. 57.

    On AMD processors, double should be aligned to an 8-byte boundary; otherwise, the CPU will perform multiple unnecessary load operations. On different processors, accessing an unaligned double may instantly crash the program.

  58. 58.

    In an empirical test on a similar algorithm, a map with a custom allocator improved the whole program by 25%. A general strategy is to reserve memory in chunks and free them with some degree of laziness.

  59. 59.

    For a complete reference, consider the GNU manual http://gcc.gnu.org/onlinedocs/cpp.pdf.

  60. 60.

    It can be applied only to a macro argument, not to arbitrary text.

  61. 61.

    Some C libraries, for example, list all the possible error codes without specifying the exact nature of these constants. In this case, they should be used as enums. In particular, it should be safe to undefine them, if they happen to be macros, and to replace them with real enumerations.

  62. 62.

    This example will actually be clear only after reading Section 2,2.

Author information

Authors and Affiliations

Authors

Rights and permissions

Reprints and permissions

Copyright information

© 2015 Davide Di Gennaro

About this chapter

Cite this chapter

Gennaro, D.D. (2015). Templates. In: Advanced Metaprogramming in Classic C++. Apress, Berkeley, CA. https://doi.org/10.1007/978-1-4842-1010-9_1

Download citation

Publish with us

Policies and ethics