0

I thought that a function template parameters are declared only by class identifiers, for example:

template<class T1, class T2> void fun(T1 a, T2 b){}

but I found other example where fundamental type can be used as a parameter:

template<int R, int C> 
void fun(double (&arr)[R][C])
{
     for(int i = 0; i < R; ++i)
     {
          for(int j = 0; j < C; ++j)
          {
               cout<<arr[i][j]<<" ";
          }
          cout<<endl;
     }
}

A function execution looks like this:

fun(myArray);

How that mechanism works ? Could you give me another examples where a fundamental type can be used as a function template parameter ?

Irbis
  • 7,699
  • 5
  • 33
  • 56

3 Answers3

2

In my travels, I have found three primary uses for template parameters as fundamental types:

One is building a function template that takes a C-style array. That's what you've posted here, but more commonly I have seen this applies to char arrays, as with:

template <size_t N, typename Char>
string MakeString (Char const (&chars)[N])
{
    return string (chars, N); 
}

int main()
{
    string hi = MakeString ("Hello");
    cout << hi; 
}

Another use is building a kind of cheap attribute system using template metaprogramming. For example, suppose you have a bunch of classes meant to represent messages in some wire protocol, and for testing purposes you want to compare the actual size of the message class with what the specifications say the size should be.

enum MsgType
{
    MsgType_Foo,
    MsgType_Bar
};

class FooMsg
{
    uint32_t mField;
    char mName [9];
};

class BarMsg
{
    char mPrice [8];
    static const size_t SpecSize = 8;
};

template <MsgType MT> size_t SpecSize();

template <> size_t SpecSize <MsgType_Foo> ()
{
    return 13;
}

template <> size_t SpecSize <MsgType_Bar> ()
{
    return 9;
}

int main()
{
    assert (SpecSize <MsgType_Foo> () == sizeof (FooMsg));
    assert (SpecSize <MsgType_Bar> () == sizeof (BarMsg));
}

Note that if you run this program the assertions will fail unless you do something platform-specific (like #pragma pack (push, 1)) to fix the packing. This is one of the things the tests are intended to catch!

Finally, another common use is more specific but the technique can be applied to your own code. In Boost.Tuple, and now C++11, the tuple class uses a template function get<size_t> as a means to access the elements. Here is an example taken from en.cppreference.com:

#include <iostream>
#include <string>
#include <tuple>

int main()
{
    auto t = std::make_tuple(1, "Foo", 3.14);
    // index-based access
    std::cout << "(" << std::get<0>(t) << ", " << std::get<1>(t)
              << ", " << std::get<2>(t) << ")\n";
}

I guess you could think of this as a specialization of both of previous examples. It's yet more template metaprogramming trickery that turns out to be quite useful in certain situations.

John Dibling
  • 94,084
  • 27
  • 171
  • 303
1

It seems like you might be confused by the fact that templates often look like the following:

template <class T> void Bar( T param );

There is a synonym for class in this context that is more descriptive: typename. This tells you that any typename can be used as a template parameter, including primitive types or types that are generated from a template. So instead of writing the above you can write:

template <typename T> void Bar( T param );

In addition to types you can pass some instances of types to a template as you demonstrated. This is commonly done to set an array size in a class template, but has many other uses. As Praetorian mentioned in the comments, you can find more information by searching for non-type template arguments.

Community
  • 1
  • 1
Dan O
  • 4,000
  • 5
  • 26
  • 43
1

Despite they syntax allowing1 class for template parameters, like: template <class T>, there was never any intent that they be limited to user-defined types.

In other words, you can always pass a fundamental type as a template parameter unless the user has done something in the code inside the template to prevent it, such as with a static_assert or code inside the template that invokes a member function of the passed type.

For non-type template parameters, you're allowed to specify essentially any type. The template can be instantiated with a value that is of that type, or can be converted to that type.


1. You can use typename if you prefer -- some people prefer to, since it does a better job of conveying the idea that the name of any type is allowed.

Jerry Coffin
  • 437,173
  • 71
  • 570
  • 1,035