I'm learning about a technique used to find the number of elements in an array (so that I can hopefully starting writing sorting algorithms without requiring the length of the array to be passed with the array as a parameter) but in the tutorial, this line appears as the template declaration:
template <typename T, size_t N>
I honestly didn't know you could declare multiple typenames in one template declaration, but furthermore what does "size_t N" do? is this a variable declaration inside of a template declaration as well?
size_t is a type that resembles unsigned int. Having it in the template parameter just means that you pass a size_t, not a type. N probably represents the size of an array, which is an unsigned value. For example:
template<typename T, size_t N>
void zeroArray(T (&arr)[N]) { //arr is a reference to an array
std::fill(arr, arr + N, 0); //of N elements of type T
}
int main() {
int arr[3];
zeroArray<int, 3>(arr);
}
In the example, I could have said:
zeroArray(arr);
because both template arguments are deduced.
what does "size_t N" do? is this a variable declaration inside of a template declaration as well?
Yep, basically.
Template arguments may be types or values of integer type. There are a few other things they can be, too (see [C++11: 14.3.2/1]) and I wouldn't call them "variables" per se, but...
Anyway, the values can be deduced just like types, too, so:
template <typename T, size_t N>
size_t array_size(const T (&)[N])
{
return N;
}
int main()
{
int x[5];
char y[10];
std::string z[20];
std::cout << array_size(x) << ',' << array_size(y) << ',' << array_size(z) << '\n';
}
Output: 5,10,20.
Perfectly valid.
Related
I have written this code in which if I uncomment the 2nd last line I get error - "template argument deduction/substitution failed: ". Is it because of some limit to generic functions in C++? Also my program doesn't print floating answer for the array b. Is there anything I can do for that? (sorry for asking 2 questions in single post.)
P.S: I have just started learning C++.
#include <iostream>
using namespace std;
template <class T>
T sumArray( T arr[], int size, T s =0)
{
int i;
for(i=0;i<size;i++)
{ s += arr[i];
}
return s;
}
int main()
{
int a[] = {1,2,3};
double b[] = {1.0,2.0,3.0};
cout << sumArray(a,3) << endl;
cout << sumArray(b,3) << endl;
cout << sumArray(a,3,10) << endl;
//cout << sumArray(b,3,40) << endl; //uncommenting this line gives error
return 0;
}
EDIT 1: After changing 40 to 40.0, the code works. Here is the output I get:
6
6
16
46
I still don't get the floating answer in 2nd case. Any suggestion ?
The reason is that compiler can not deduce the type for T.
How it should understand what T is for your last example? The type of the first argument (b) is double[], while it is T[] in the function definition. Therefore it looks like that T should be double. However, the type of the third argument (40) is int, so it looks like T should be int. Hence the error.
Changing 40 to 40.0 makes it work. Another approach is to use two different types in template declaration:
#include <iostream>
using namespace std;
template <class T, class S = T>
T sumArray( T arr[], int size, S s =0)
{
int i;
T res = s;
for(i=0;i<size;i++)
{ res += arr[i];
}
return res;
}
int main()
{
int a[] = {1,2,3};
double b[] = {1.0,2.0,3.1};
cout << sumArray(a,3) << endl;
cout << sumArray(b,3) << endl;
cout << sumArray(a,3,10) << endl;
cout << sumArray(b,3,40) << endl; //uncommenting this line gives error
return 0;
}
Note that I had to cast s to T explicitly, otherwise the last example will lose fractional part.
However, this solution will still not work for sumArray(a,3,10.1) because it will cast 10.1 to int, so if this is also a possible use case, a more accurate treatment is required. A fully working example using c++11 features might be like
template <class T, class S = T>
auto sumArray(T arr[], int size, S s=0) -> decltype(s+arr[0])
{
int i;
decltype(s+arr[0]) res = s;
...
Another possible improvement for this template function is auto-deduction of array size, see TartanLlama's answer.
sumArray(b,3,40)
The type of 40 is int, but the type of b is double[3]. When you pass these in as arguments, the compiler gets conflicting types for T.
A simple way to fix this is to just pass in a double:
sumArray(b,3,40.0)
However, you would probably be better off allowing conversions at the call site by adding another template parameter. You can also add one to deduce the size of the array for you so that you don't need to pass it explicitly:
template <class T, class U=T, std::size_t size>
U sumArray(T (&arr) [size], U s = 0)
The U parameter is defaulted to T to support the default value for s. Note that to deduce the size of the array, we need to pass a reference to it rather than passing by value, which would result in it decaying to a pointer.
Calling now looks like this:
sumArray(b,40)
Live Demo
In
template <class T>
T sumArray( T arr[], int size, T s =0)
^ ^
Both (deducible) T should match.
In sumArray(b, 3, 40), it is double for the first one, and int for the second one.
There is several possibilities to fix problem
at the call site, call sumArray(b, 3, 40.0) or sumArray<double>(b, 3, 40);
Use extra parameter:
template <typename T, typename S>
auto sumArray(T arr[], int size, S s = 0)
Return type may be T, S, or decltype(arr[0] + s) depending of your needs.
make a parameter non deducible:
template <typename T> struct identity { using type = T;};
// or template<typename T> using identity = std::enable_if<true, T>;
template <typename T>
T sumArray(T arr[], int size, typename identity<T>::type s = 0)
Should be
sumArray(b,3,40.0)
so, T will be deduced to double. In your code it's int.
Another option when deduction fails is to explicitly tell the compiler what you mean:
cout << sumArray<double>(b,3,40) << endl;
The compiler does not know whether T should be int or double.
You might want to do the following, in order to preserve the highest precision of the types passed:
template <class T, class S>
std::common_type_t <T, S> sumArray (T arr [], std::size_t size, S s = 0)
{
std::common_type_t <T, S> sum = s;
for (std::size_t i = 0; i != size; ++i)
{
sum += arr[i];
}
return sum;
}
The function you are writing, however, already exists. It's std::accumulate:
std::cout << std::accumulate (std::begin (b), std::end (b), 0.0) << std::endl;
Templates only accept one type of data, for example if you send an array of double, then the runtime will deduce :
Template = double[]
so every time he will see it he will expect an array of doubles.
sumArray(b,3,40) passes "b" (which is an array of doubles) but then you pass "40" which the runtime cannot implicitly convert to double.
So the code
sumArray(b,3,40.0)
will work
Just wondering - i've written following function
template <class T, size_t N>
T* asFlatArray (T arr[][N])
{
// some code
}
and call it like
asFlatArray(myArray); // where myArray is int myArray[some_size][sime_size];
compilation runs without errors. But if i change 'arr' to reference like
template <class T, size_t N>
T* asFlatArray (T (&arr)[][N])
{
// some code
}
i'll have errors:
parameter ‘arr’ includes reference to array of unknown bound ‘T [][N]’
I know how to fix it
template <class T, size_t Rows, size_t Columns>
T* asFlatArray (T (&arr)[Rows][Columns])
but i don't understand why is it's happening?
The fundamental problem is that the first function template is equivalent to
template <class T, size_t N>
T* asFlatArray (T (*arr)[N]) {}
so there is no dimension to calculate. It will match a pointer to an array, or anything that can decay to pointer to array. Thus is can also match 2D array.
As for the second example, a reference or pointer to type T[][N] cannot be a function parameter, which leads to a compiler error. Of course, you can add an extra template parameter for the missing dimension:
template <class T, size_t N, size_t M>
T* asFlatArray(T (&arr)[N][M]) {}
C++11 and C++14 standards in part [dcl.fct]/8 (8.3.5) explicitly state:
If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed.
There is actually a core language active issue proposing to remove that from the standard, so perhaps we won't see it in C++17.
BTW, if the array size is really unknown, clang (but not gcc) compiles the code:
#include <iostream>
class A {
public:
static int arr[][3];
};
// this compiles on clang but not gcc
void func(int (&arr_ref)[][3]) {
std::cout << arr_ref[1][2] << std::endl;
}
int main() {
int (&arr_ref)[][3] = A::arr;
std::cout << A::arr[1][2] << std::endl;
arr_ref[1][2] = 100;
std::cout << A::arr[1][2] << std::endl;
arr_ref[1][2] = 200;
func(arr_ref);
}
int A::arr[][3] = {{1, 2, 3}, {4, 5, 6}};
Demo
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 ?
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.
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.
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.
I have a template method as follows:-
template<typename T, int length>
void ProcessArray(T array[length]) { ... }
And then I have code using the above method:-
int numbers[10] = { ... };
ProcessArray<int, 10>(numbers);
My question is why do I have to specify the template arguments explicitly. Can't it be auto-deduced so that I can use as follows:-
ProcessArray(numbers); // without all the explicit type specification ceremony
I am sure I am missing something basic! Spare a hammer!
You can't pass arrays by value. In a function parameter T array[length] is exactly the same as T* array. There is no length information available to be deduced.
If you want to take an array by value, you need something like std::array. Otherwise, you can take it by reference, which doesn't lose the size information:
template<typename T, int length>
void ProcessArray(T (&array)[length]) { ... }
You're missing the correct argument type: arrays can only be passed by reference:
template <typename T, unsigned int N>
void process_array(T (&arr)[N])
{
// arr[1] = 9;
}
double foo[12];
process_array(foo); // fine
This question already has answers here:
How does this template magic determine array parameter size?
(3 answers)
Closed 6 years ago.
template<typename T, size_t n>
size_t array_size(const T (&)[n])
{
return n;
}
The part that I don't get is the parameters for this template function. What happens with the array when I pass it through there that gives n as the number of elements in the array?
Well, first you have to understand that trying to get a value out of an array can give you a pointer to its first element:
int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost
References refer to objects using their exact type or their base-class type. The key is that the template takes arrays by reference. Arrays (not references to them) as parameters do not exist in C++. If you give a parameter an array type, it will be a pointer instead. So using a reference is necessary when we want to know the size of the passed array. The size and the element type are automatically deduced, as is generally the case for function templates. The following template
template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
return n;
}
Called with our previously defined array a will implicitly instantiate the following function:
size_t array_size(const int (&)[3]) {
return 3;
}
Which can be used like this:
size_t size_of_a = array_size(a);
There's a variation I made up some time ago [Edit: turns out someone already had that same idea here] which can determine a value at compile time. Instead of returning the value directly, it gives the template a return type depending on n:
template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];
You say if the array has n elements, the return type is a reference to an array having size n and element type char. Now, you can get a compile-time determined size of the passed array:
size_t size_of_a = sizeof(array_size(a));
Because an array of char having n elements has sizeof n, that will give you the number of elements in the given array too. At compile time, so you can do
int havingSameSize[sizeof(array_size(a))];
Because the function never is actually called, it doesn't need to be defined, so it doesn't have a body. Hope I could clear the matter up a little bit.
Think of it this way, suppose you had a bunch of functions:
// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
return 1;
}
size_t array_size(const int (&)[2])
{
return 2;
}
size_t array_size(const int (&)[3])
{
return 3;
}
// etc...
Now when you call this, which function gets called?
int a[2];
array_size(a);
Now if you templatize the arraysize, you get:
template <int n>
size_t array_size(const int (&)[n])
{
return n;
}
The compiler will attempt to instantiate a version of array_size that matches whatever parameter you call it with. So if you call it with an array of 10 ints, it will instantiate array_size with n=10.
Next, just templatize the type, so you can call it with more than just int arrays:
template <typename T, int n>
size_t array_size(const T (&)[n])
{
return n;
}
And you're done.
Edit: A note about the (&)
The parentheses are needed around the & to differentiate between array of int references (illegal) and reference to array of ints (what you want). Since the precedence of [] is higher than &, if you have the declaration:
const int &a[1];
because of operator precedence, you end up with a one-element array of const references to int. If you want the & applied first, you need to force that with parentheses:
const int (&a)[1];
Now the you have a const reference to a one element array of ints. In the function parameter list, you don't need to specify the name of a parameter if you don't use it, so you can drop the name, but keep the parentheses:
size_t array_size(const int (&)[1])
Nothing happens to the array. It's an unused parameter that is used to resolve the signature of the template function.
It also cannot be used as a template argument, but that's a separate nit.
A little weird way to get the result as compile-time const for those of us who don't have "constexpr":
#include <iostream>
namespace
{
template <size_t V>
struct helper
{
enum
{
value = V
};
};
template<typename T, size_t Size>
auto get_size(T(&)[Size]) -> helper < Size >
{
return helper < Size >() ;
}
template<typename T>
struct get_value
{
enum
{
value = T::value
};
};
}
int main()
{
std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}