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

69 comments to 13.4 — Template non-type parameters

  • Pete Dubler

    This series of tutorials has really helped up my programming game.  This one in particular was another major breakthrough for me.  Thank you!

    Now that I have created a set of template class object using a non-type argument, I need to create an array of these objects so I can use a for loop to step through these objects.  Here is an example:

    Now I have done this before with class objects (see example below) but the non-type template parameter arraysize is keeping me from doing this since the objects have different arraysizes.  

    for example, the following works fine:

    So...How does one create the list (array) of object when we have different template parameters, in this case arrarysize values?

    • nascardriver

      If your types have different template arguments (Doesn't matter if they are type or non-type arguments), they are entirely different types. You cannot store them in an array, just like you can't store an `int` and a `float` in the same array.

      You can write an interface for lcd fields and have `LCDfield` inherit from it. Then you can have an array of the interface type.

      • Peter Dubler

        Thank you for your reply and, again, thank you for these tutorials.

        I am not familiar with the “interface” concept.  Could you point me to a tutorial or example on that?

        (Being able to loop through each of the objects let’s me refresh TFT screens with highly dynamic and asynchronous data field changes, with a minimum of code.  I was doing that just fine but now I am trying to squeeze down memory usage by keeping my data char arrays as small as possible for each field).

        • nascardriver

          Interfaces are covered in chapter 12. They are classes that only contain pure virtual functions.

          • Peter Dubler

            Indeed I read the section on interfaces but I do not understand how to apply that here.  I think I need an example which actually shows the sort of iteration I am hoping to do.

            For example, I may have three screens of data, each using a few objects of the LCDfild class, but none of the screens using all of them. (eg. screen1 uses LCDfield->fA, fB, and fC. while screen2 uses LCDfield->fS2A and fS2B.  To draw all the fields of screen1, I need to iterate through the Update methods fA.Update, fB.Update, and fC.Update.  For screen2, fS2A.Update, fS2B.  Of course, in reality I have many more fields per screen, and other than when I have to do a full screen refresh, I am just updating the changing field).

            I could be making this too complicated by trying to assign (and lock down) the size of the char array members, but keeping my memory usage consistent and not worrying about leaks or swisscheesing due to dynamic assignment is my objective.  Am I over-thinking this?  Should I just use string type and pass fixed sized strings in the construction of each object instead of using the template with the arraysize non-type parameter?

          • Pete Dubler

            Well, researching further, with the valuable clue of using a interface class, I found the following link, adopted the third answer there, and came up with the following code. https://stackoverflow.com/questions/9961329/c-iterating-through-objects-to-call-the-same-method

            This saved me 500 bytes of dynamic memory, almost half of what I was using before adopting the non-type parameter for arraysize.

            Paying it forward, and hoping others might benefit, here is the code I ended up with (enough shown to demonstrate one use of the for loop (and there are plenty others in my code).

  • Virginie

    Hi,
    I'm using a template library who has two parameters: a template type parameters and a non-type parameter. In my code, I can define the latter only at runtime and not at compile-time. How can I manage this, knowing that the non-type parameter is in a restricted range of values that I know in advance?

    • nascardriver

      Write a wrapper that calls the appropriate function based on a run-time variable

      • Virginie

        Thank you.
        I tried what you suggested to me, but I get an error : inconsistent deduction for auto return type: ‘Eigen::Tensor<double, 1>’ and then ‘Eigen::Tensor<double, 2>’
        []#include <iostream>
        #include "Dependecies/unsupported/Eigen/CXX11/Tensor"

        auto callFunction(int i)
        {
        switch (i)
        {
            case 1:
                Eigen::Tensor<double, 1> T1;
                return T1;
            case 2:
                Eigen::Tensor<double, 2> T2;
                return T2;
            case 3:
                Eigen::Tensor<double, 3> T3;
                return T3;
            case 4:
                Eigen::Tensor<double, 4> T4;
                return T4;
            case 5:
                Eigen::Tensor<double, 5> T5;
                return T5;
            case 6:
                Eigen::Tensor<double, 6> T6;
                return T6;
            case 7:
                Eigen::Tensor<double, 7> T7;
                return T7;
            case 8:
                Eigen::Tensor<double, 8> T8;
                return T8;

        }
        }

        int main() {

        auto MyTens=callFunction(2);

        return 0;
        } [/code]

        • nascardriver

          Ah, I thought you were talking about template functions, my bad. Unless the `Eigen::Tensor`s have a common base class, you can't mix them.
          Assuming all `Eigen::Tensor` have the same interface (Same functions etc.) but don't have a common base, you can create somewhat of a base yourself.

          • Virginie

            thank you so much for helping! I still have one last question.
            In order to access the methods of Eigen::Tensor, I thought to create a getter in MyTensor that would have returned m_tensor, but since makeTensor is a unique_ptr of Base, I can't access to those methods! How can I manage this?

            • nascardriver

              You can't. You might be able to access all tensors in the same way, but they are entirely different types. Add the functions that you want to use to `Base` and use `Base` instead of using the tensors directly.

              Another way of using mixed types is `std::variant`, but you have to access the tensors through `std::visit`.

  • salah

    Hi,
    I am getting error here : "cannot call not-constexpr function", so why(const int) is not valid, why should I use constexpr instead, are not they the same in this case ?
    thanks in advance

    • nascardriver

      No, `const` and `constexpr` are different. `const` only means that the value can't change after initialization. `constexpr` additionally means that the value can be determined at compile-time.
      To create a type, you can't use anything that depends on the run-time, but `return5()` depends on the run-time (It is not `constexpr`).
      It's not covered on learncpp, but functions can be `constexpr` too

      The `constexpr` doesn't apply to the return type but to the function, adding additions restrictions as to what you can do in the function.

  • marius

    I want this to work. I guess my operator* member is not right, or for instruction expects something else to determine element.

    • nascardriver

      You cannot access `m_data[size]`, there is no such element. Accessing it causes undefined behavior. Instead, calculate where that element would be if it existed

      The range-based for-loop already performs an indirection of the iterators. `element` is an `int`, not an `int*`. You can change the loop to use references instead

      Then you can modify the elements.

  • Daniel

    Wouldn't you want to dynamically allocate data for m_array in case you wanted to pass a integer variable to the non-type parameter instead of an integer literal?

    • nascardriver

      You can't use run-time variables as non-type parameters. The parameters are a part of the type and have to be known at compile-time.

  • 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]