Search

13.4 — Template non-type parameters

In previous lessons, you’ve learned how to use template type parameters to create functions and classes that are type independent. However, template type parameters are not the only type of template parameters available. Template classes and functions can make use of another kind of template parameter known as a non-type parameter.

Non-type parameters

A template non-type parameter is a special type of parameter that does not substitute for a type, but is instead replaced by a value. A non-type parameter can be any of the following:

  • A value that has an integral type or enumeration
  • A pointer or reference to a class object
  • A pointer or reference to a function
  • A pointer or reference to a class member function
  • std::nullptr_t

In the following example, we create a non-dynamic (static) array class that uses both a type parameter and a non-type parameter. The type parameter controls the data type of the static array, and the non-type parameter controls how large the static array is.

This code produces the following:

11 10 9 8 7 6 5 4 3 2 1 0
4.4 4.5 4.6 4.7

One noteworthy thing about the above example is that we do not have to dynamically allocate the m_array member variable! This is because for any given instance of the StaticArray class, size is actually constant. For example, if you instantiate a StaticArray<int, 12>, the compiler replaces size with 12. Thus m_array is of type int[12], which can be allocated statically.

This functionality is used by the standard library class std::array. When you allocate a std::array<int, 5>, the int is a type parameter, and the 5 is a non-type parameter!

13.5 -- Function template specialization
Index
13.3 -- Template classes

50 comments to 13.4 — Template non-type parameters

  • CHERIS

    What is difference between typename and class in tamplate.

  • Anthony

    I'm trying to implement a ring buffer, so came back to revise template syntax and declaration order. However, I can't understand why the following doesn't work:

    Could you be incredibly kind and tell me what I'm doing wrong?

    -- Anthony

    • `elem_type` only exists inside `ring_iterator`. `ring` isn't inside of `ring_iterator`, so it doesn't know what `elem_type` is.

      The `T` in line 8 is the `T` from line 4. Line 8 doesn't introduce new templates.

      If you want a `ring_iterator` over the same type as `ring` to be a friend of `ring`, you need to use `typename T::value_type` in line 8.

      This will only befriend `ring_iterators` whose `elem_type` is `T::value_type`.

      If you want every `ring_iterator` to be a friend of `ring`, you need another template declaration before befriending `ring_iterator`.

      Line 12 will also cause an error, you already declared a default template argument in line 1, you're not allowed to re-declare it here. Remove ` = typename T::value_type`.

      • Anthony

        Thanks so much for helping.

        >If you want a `ring_iterator` over the same type as `ring` to be a friend of `ring`, you need to use `typename T::value_type` in line 8.
        >1| friend class ring_iterator<T, typename T::value_type>;

        Done. But of course the code will still not compile because, as you point out, `ring` isn't inside of `ring_iterator`, so it doesn't know what `elem_type` is. So what whould I do? Should/must I put `ring` inside `ring_iterator`. I don't really want to..

        2) >>If you want a `ring_iterator` over the same type as `ring` to be a friend of `ring`, you need to use `typename T::value_type` in line 8.
        Yes, this is what I'm trying to achieve. Only these iterators should be friends of `ring`.

        I'm still a bit stuck. I'm not sure what question to ask!

        • > Done. But of course the code will still not compile
          You're doing something else wrong then. This code compiles

          • Anthony

            It took me a while to track the bug down. To demonstrate it, I've just added instantiation of a ring object:

            Here are the errors:

            It's complaining because I'm trying to make T an int?

            EDIT:

            works.. but in my actual code I'm getting a very similar error.. I'll look further into it.

            • Anthony

              OK, I've stripped down what I have (please see below), and the error is:

                                     ^

              • - Initialize your variables with brace initializers, especially in line 24.
                - Limit your lines to 80 characters in length for better readability on small displays.
                - Don't use `std::uint*_t`, use `std::uint_least*_t` or `std::uint_fast*_t`.
                - Use `using` instead of `typedef`, it has a more intuitive syntax.

                Line 44 sets `ring::T` to `uint8_t`. Line 25 tries to access `uint8_t::value_type`. `uint8_t` doesn't have members.

                `ring_iterator::value_type` should be the same as the `value_type` of the `ring` that created this `ring_iterator`.
                Think about what `ring_iterator` needs. Right now, you're giving it 2 types, one of which is unused.
                The iterator needs to know the type of the elements it's iterating over and it needs to know where to start. It doesn't need to know the index of the element it's currently at.
                The element's type is a template parameter. It's set by `ring` from `ring::value_type`. `ring_iterator` creates a `value_type` pointer which is used as the internal iterator (You can use ++, --, etc. on the pointer to step through the ring). The iterator doesn't know where the ring starts or stops, it's up to the user to verify they're not leaving the ring by comparing to `ring::begin()` and `ring::end()`.
                When you instantiate a `ring_iterator` from within `ring`, you pass a pointer to the first element in your ring.

    • Anthony

      Thank you nascardriver. Yes, it all makes sense now. I was trying to mash information from several sources, and really I need to do more work on template syntax.

      Much appreciated.

  • topherno

    Hey Alex!

    Is it possible to have a template class with no type parameter and ONLY a non-type parameter? For example, I tried something along the lines of:

    MyArray.h

    main.cpp

    and it compiled and run just fine...
    Basically, I need an int array that can be different sizes, but I want its size to be set at compile time and not run time (because I want to use std::array instead of std::vector). Is the above solution adequate in your view or am I missing something obvious? I found an alternative solution in this link:

    https://stackoverflow.com/questions/874298/c-templates-that-accept-only-certain-types

    The accepted answer's solution of "declaring, but not defining..." is pretty neat as well. Thoughts?

    • It's totally fine to have only non-type parameters.
      If you're not planning on adding any other members to @MyArray, you might as well declare it a child of @std::array.

  • Gio

    When compiler encounters a call to the templated classes:

    Does compiler creates two instance of the class StaticArray one for 12 integers array and second for 5 integers array?

      • Gio

        How does compiler distinguishes these created two class instances with the same name?
        Let`s say we have a templated class:

        Than compiler encounters a call to the templated classes:

        After that compiler stencils out a copy of two class instances with the same class names:

        But compiler doesn`t throws the 'class' type redefinition error.

  • sam

    Typo (an instead of a): "a type parameter and an non-type parameter..." --> "a type parameter and a non type parameter..."

    Also in a comment within the code snippet: "for a class with an non-type parameter..." --> "for a class with a non-type parameter...".

  • Xarxos

    Hi, thanks for the lesson! I was just wondering about the utility of this. I understand that it allows us to design classes using a static array without having to rely on dynamic memory, but is that the only advantage? In many situations it seems you could achieve the same result by using a regular parameter in a constructor, so are there any other general cases where a non-type parameter is advantageous?

  • Val

    "Template classes (not template functions) can make use of another kind of template parameter known as an expression parameter."

    Why "not template functions"?

    class Grade:

    Function template:

    Main function:

    Looks like all work!
    As usual, thanks a lot!

    • Alex

      I'm not sure, it does appear to work just fine, and I can't find any documentation to support otherwise. I've updated the lesson accordingly. Thanks for pointing this out.

  • Omri

    "...if you instantiate *an* StaticArray<int, 12>..."
    =>
    "...if you instantiate *a* StaticArray<int, 12>..."

  • Curiosity

    An expression parameter can be any of the following :-
    •A pointer or reference to a class object
    •A pointer or reference to a function
    •A pointer or reference to a class member function

    Can u please give an example of each of them ?
    Thanks In Advance

    • Alex

      I could, but I'm not going to for several reasons:
      1) Finding concise examples using these types of expression parameters is challenging
      2) These are almost never used
      3) You should be able to figure this out yourself. :) (just replace the "int" in the template definition with a pointer or reference to a class, function, or member function)

  • Chris

    Hey Alex.

    Would there be a way to have the user input a custom value for the size of the array?
    Doing something like this:

    int main()
    {
        std::cout << "Size of array = ";
        int x;
        do
        {
            std::cin >> x;
            if (std::cin.fail())
            {
                std::cin.clear();
                std::cin.ignore(32767, '\n');
                x = -1;
            }
        } while (x <= 0);
        StaticArray<int, x> intArray;
    }

    results in an error due to x being non-const.

    Is there a way to fix this?

    Thanks

  • Mike

    You mentioned "When you allocate a std::array, the int is a type parameter, and the 5 is an expression parameter!"
    What do you mean by the 5? I'm taking this to assume something like this:

  • Mauricio Mirabetti

    Alex, I believe there's a typo on two places:

    and:
    11 10 9 8 7 6 5 4 3 2 1 0
    4.4 4.5 4.6 4.7
    Hello there! // perhaps a left over of an old version example?

    Best regards.
    Mauricio

  • Vivian

    Hi Alex,

    Since the example could be rewritten with a constructor to create m_array, how do you decide which would be appropriate (i.e. when would you use a template expression and when would you use a constructor)?

    Thanks!

    • Alex

      Generally, template expression parameters are a good choice when you can live with static memory allocation (e.g. arrays that are sized at compile time). Having the constructor create m_array requires dynamic memory allocation, which adds complexity (but also allows other things such as dynamic resizing and the memory comes from the heap instead of the stack, which is beneficial for large arrays).

      So it's really just a matter of tradeoffs.

  • Matt

    Under section "Expression parameters", in the code example, you wrote a comment:
    "// declare an integer buffer with room for 12 chars".

    Did you mean to write "12 integers"?

    Also, in the same code example, towards the end of main(), you used function strcpy_s() to copy the string into your buffer. You may or may not have gone over the use of this function in a previous lesson(I can't recall), and it's easy enough to look it up online(which I had to do), but I think a comment in the code explaining it's use would be very helpful, even though it's fairly obvious what the function is accomplishing. Also, what header file needs to be included to use this function?

    • Alex

      Yes, 12 integers. I updated the comment about strcpy_s -- but I'll clarify further when I rewrite this lesson. I used to cover strcpy_s in earlier lessons but have largely deprecated discussion of working with C-style strings in favor of using std::string. I'll have to figure out how to resolve (either recover that content prior to this lesson, or update this lesson with a different example).

      Thanks for pointing that out.

  • Dave

    #include <cstdlib>
    #include <iostream>
    #include <string>

    template <typename T, int nSize> // nSize is the expression parameter
    class Buffer
    {
    private:
        // The expression parameter controls the size of the array
        T m_atBuffer[nSize];

    public:
        T* GetBuffer() { return m_atBuffer; }
        
        T& operator[](int nIndex)
        {
            return m_atBuffer[nIndex];
        }
    };

    template<int nSize>
    void PrintBufferString(Buffer<char, nSize> &rcBuf)
    {
        
    std::cout << rcBuf.GetBuffer() << std::endl;
    }

    int main()
    {
        // declare an integer buffer with room for 12 chars
        Buffer<int, 12> cIntBuffer;

        // Fill it up in order, then print it backwards
        for (int nCount=0; nCount < 12; nCount++)
            cIntBuffer[nCount] = nCount;

        for (int nCount=11; nCount >= 0; nCount--)
            std::cout << cIntBuffer[nCount] << " ";
        std::cout << std::endl;

        // declare a char buffer with room for 31 chars
        Buffer<char, 31> cCharBuffer;

        // strcpy a string into the buffer and print it
        strcpy_s(cCharBuffer.GetBuffer(), 31, "Hello there!");
        std::cout << cCharBuffer.GetBuffer() << std::endl;

        return 0;
    }

    I tried to compile this, but it says strcpy_s was not declared in this scope. I use netbeans. What am I missing? I've tried using cstring as well.
    Thanks!

  • SJ

    Hi Alex,

    Regarding the following:

    "One noteworthy thing about the above example is that we do not have to dynamically allocate the m_atBuffer member array! This is because for any given instance of the Buffer class, nSize is actually constant. For example, if you instantiate a Buffer, the compiler replaces nSize with 12. Thus m_atBuffer is of type int[12], which can be allocated statically."

    Since we are not dynamically allocating the array, does that mean it is being declared on the stack?

  • Kiran C K

    I have a couple of questions:

    1) In the first example, there are no constructors, so how can cIntBuffer assign its value to m_atBuffer?

    2) m_atBuffer is a private member. So, shouldn't that cause a "m_atBuffer is private" error when it is instantiated in main() as above?

    3) Reference must be initiated in the same line. Can we just do that inside the template parameter declaration, if we use reference as expression parameter?

    • Alex

      1) There is no assignment. When the template class is instantiated by cIntBuffer (with nSize=12), all instances of nSize in the class are textually replaced with the value 12, which is then compiled in. This is why we're able to treat it as a compile-time constant.
      2) No. We instantiate cIntBuffer in main(). Because we don't have any constructors, C++ creates an empty default constructor and calls that, which does nothing. So the array m_atBuffer (of type int and size 12) is actually uninitialized to start.
      3) I'm not actually sure. Try it and see if it works. :)

      • Kiran C K

        I tried. It shows 2 errors at multiple lines wherever I have used the template. I have used the reference as the second argument

        1)template argument 2 is invalid
        2)'Print' is not a member function

  • JaSoN

    When I try to do this : T& GetBuffer() { return m_atBuffer; }, the compiler report an error. So can you tell me why we use T* GetBuffer() { return m_atBuffer; }? Thank you !!

    • Alex

      In this case, T is of type int, and m_atBuffer is an array of integers. This function returns a pointer to the integer array.

      If you return an element of type T&, then you're trying to return an reference to a single integer, which doesn't make sense in this context.

  • Steve

    Well Alex, there are tons of comments of gratitude but I would like to thank you as well. Great, straightforward tutorial to get us newbies through the basics with spot-on examples. I would like to ask you three things.
    1. Do you have plans on releasing these tutorials as a book. If so, are you going to get a more in-depth look on the standart library as your examples seem to be superior than those in other books. You put a great deal of thought in delivering examples understandable for everyone.
    2. Are you going to get a lesson or two on binary trees and linked lists? Those are particularly interesting.
    3. Is it possible to give a tutorial on game developing including either SDL, OpenGl or Direct3D just to get us through the basic idea of a game structure. This question comes from some examples you have on games and random number generation.
    Thank you.

    • Alex

      1) No, but I'd like to eventually write more tutorials on the standard library functionality.
      2) I'd like to also write some lessons on data structures.
      3) I'd also like to write some basic lessons on SDL and OpenGL.

      The problem is simply one of time. I spend a good part of my limited time answering questions instead of writing articles. Maybe I should focus more on a new content...

  • Sean

    Alex,
    I just finished your awesome tutorial, I felt my understanding to C++ really improved a lot.
    Would you please recommand a couple of C++ books because I really want to keep all the learnings up and may dig more into it?
    I am a VBA programmer for a while and want to be involved in C++ programming work. I think the only way to improve C++ programming skill is to do the real world programming. I am not a CS major. Is there any tests that can tell employers my C++ level and help me land with some entry level C++ programmer?

    Thanks a lot,

    Sean,

    • I'm not really sure what to recommend to you bookwise, as you'll find a lot of books are rather redundant with this tutorial. Instead, I would highly advise assigning yourself a project that will test your skills and make you put the concepts you've learned into play. You'll learn more from doing that than anything at this point.

      I do not know of any standardized C++ proficiency tests. Many employers have their own versions and will have you take them when you apply (if they like your resume enough).

Leave a Comment

Put all code inside code tags: [code]your code here[/code]