I've a very simple template function, but there's confusion as to how to instantiate/call the function because of the non-type parameter.
The definition of the template function is as goes:
template<typename Glorp, int size>
Glorp min(Glorp array[size])
{
Glorp minival = array[0];
for (int i = 0; i < size; i++)
if (array[i] < minival)
minival = array[i];
return minival;
}
Now, in main() I have the following code:
void main()
{
const int size=5;
int array[size];
for (int i = 0; i < size; i++)
cin >> array[i];
int p = min(array[size]);
cout << p;
}
This gets me the error message:
Error 1 error C2783: 'Glorp min(Glorp *)' : could not deduce template argument for 'size' c:\users\tamara\documents\visual studio 2013\projects\nuevoprojecto\nuevoprojecto\main.cpp 23 1 NuevoProjecto
How DO I call this function from main()? I can't find the answer for this, the only examples I saw were for non type parameters in template classes
I see two major problems in you code
1) the syntax for a template function receiving an array, deducing the type and the size, is the following
template <typename Glorp, int size>
Glorp min (Glorp (&array)[size])
{
// ...........^^^^^^^^
}
2) you have to call it without [size]
int p = min(array[size]); // wrong
int p = min(array); // correct
because passing array[size] you're trying to pass a single int from an un-allocated memory position (correct array values are from array[0] to array[size-1]).
A minor problem: main() return a int, not a void.
Off topic suggestion: if you can use at least C++11, consider using std::array, instead of old C-style arrays, whenever possible.
Related
I am trying to implement an API layer for a C++ project, here's a small example of what I want to achieve:
double data[8] = {0,1,2,3,4,5,6,7};
template<typename T>
void cpy(T *buf){
for(int i=0; i<8; i++)
buf[i] = (T)data[i];
}
int main() {
int a[8];
cpy(a);
float b[8];
cpy(b);
double c[2][4];
cpy(c); //error: functional cast to array type 'double [4]'
return 0;
}
The idea is to allow the user to use the function cpy() for different types of array without having to do cpy<double>(c) or cpy((double *)c) but in this example, calling cpy() with a 2D array leads to compilation error:
error: expected initializer before 'cpy'
In instantiation of 'void cpy(T*) [with T = double [4]]':
required from here
error: functional cast to array type 'double [4]'
How can we achieve this?
Assuming you cannot change main() (except for typo with missing ;).
You can add overload:
template<typename T>
void cpy(T *buf){
for (int i = 0; i != 8; ++i) {
buf[i] = data[i];
}
}
template<typename T, std::size_t N>
void cpy(T (*buf)[N]){
cpy(&buf[0][0]);
}
Demo
I have the following code which could not be complied.
using namespace std;
void f(int);
template<typename T1, size_t N>
void array_ini_1d(T1 (&x)[N])
{
for (int i = 0; i < N; i++)
{
x[i] = 0;
}
}
What is the proper way to pass the array if the main is something like below.
int main()
{
int a;
cin >> a;
int n = a / 4;
f(n);
return 0;
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
error: no matching function to call to array_ini_1d..............
The problem is that variable size arrays are not supported by c++, and is only supported as compilers extension. That means, the standard doesn't say what should happen, and you should see if you can find in compiler's documentation, but I doubt that such corner cases are documented.
So, this is the problem :
int arr[n];
The solution is to avoid it, and use something supported by c++, like for example std::vector.
I don't think the compiler can deduce the size of a variable-length array in a template. Also, don't forget to forward declare f before you use it. Variable-length arrays are a GCC extension and you should get a warning regarding their use.
You may declare your function like this:
template <typename A, size_t N> void f(A a[N]) {
for(size_t i = 0; i < N; i++)
cout << a[i];
}
However, the problem is that when you call the function, the compiler won't deduce the template parameters, and you will have to specify them explicitly.
char arr[5] = {'H', 'e', 'l', 'l', 'o'};
int main()
{
//f(arr); //Won't work
f<char, sizeof(arr)/sizeof(arr[0])>(arr);
cout << endl;
return 0;
}
Unfortunately, that ruins the very idea...
UPD: And even that code does NOT work for an array that has variable length, for the length is calculated at runtime, and the template parameters are defined at compilation time.
UPD2: If using std::vector you may create it initialized:
vector<int> arr(n, 0);
Or you may fill it with fill from <algorithm> when needed:
std::fill(arr.begin(), arr.end(), 0);
As you use Variable length array (VLA) (compiler extension), compiler cannot deduce N.
You have to pass it by pointer and give the size:
template<typename T>
void array_ini_1d(T* a, std::size_t n)
{
for (std::size_t i = 0; i != n; ++i) {
a[i] = 0;
}
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
Or use std::vector. (no extension used so). Which seems cleaner:
template<typename T>
void array_ini_1d(std::vector<T>& v)
{
for (std::size_t i = 0, size = v.size(); i != n; ++i) {
a[i] = 0; // or other stuff.
}
}
void f(int n)
{
std::vector<int> arr(n); // or arr(n, 0).
array_ini_1d(arr);
}
Template parameters must be resolved at compile-time.
There is no way that a function template with parameter size_t N can match any sort of array or other container whose size comes from a run-time input.
You will need to provide another version of the array_1d_ini which does not have the size as a template parameter.
template<typename T, size_t N>
void f(T* a)
{
/* add your code here */
}
int main()
{
int a[10];
f<int, 10>(a);
return 0;
}
#include <iostream>
#include <string>
using namespace std;
template <class T> int IsSubArr(T& a, int a_len, T& b, int b_len)
{
int i,j;
bool found;
int k;
T& s=a,l=b;
int s_len = (a_len < b_len) ? a_len : b_len; // find the small array length
if (s_len == a_len) // check to set pointers to small and long array
{
s = a;
l = b;
}
else
{
s = b;
l = a;
}
for (i = 0; i <= a_len-s_len; i++) //loop on long array
{
found = true;
k=i;
for (j=0; j<s_len; j++) // loop on sub array
{
if (s[j] != l[i])
{
found = false;
break;
}
k++;
}
}
if (found)
return i;
else
return -1;
}
/******* main program to test templates ****/
int main()
{
int array[5] = {9,4,6,2,1};
int alen = 5;
int sub_arr[3] = {6,2,1};
int slen = 3;
int index= 0;
index = IsSubArr(array,alen,sub_arr,slen);
cout << "\n\n Place of sub array in long array: " << index;
cout << endl;
return 0;
}
for this line of code:
index = IsSubArr(array,alen,sub_arr,slen);
i get error:
Error 1 error C2782: 'int IsSubArr(T &,int,T &,int)' : template parameter 'T' is ambiguous
please help to resolve this issue ?
Since array[a] and array[b] where a != b are 2 different types, you'll need 2 type templates args.
A work around would be to use pointers.
+ template <class T> int IsSubArr(T* a, int a_len, T* b, int b_len)
+ T* s = a; T*l = b;
You defined the first and the third parameters as references
template <class T> int IsSubArr(T& a, int a_len, T& b, int b_len)
^^^^ ^^^^
and pass as arguments for these parameters two arrays with different types
int array[5] = {9,4,6,2,1};
int sub_arr[3] = {6,2,1};
//...
index = IsSubArr(array,alen,sub_arr,slen);
^^^^^ ^^^^^^^
The first argument has type int[5] and the third argument has type int[3]
So the compiler is unable to deduce the referenced type T.
If you are going to use arrays with the function then you could declare it like
template <class T, size_t N1, size_t N2>
int IsSubArr( T ( &a )[N1], T ( &b )[N2] );
Or you could use pointers instead of the references to arrays
template <class T> int IsSubArr( T *a, size_t a_len, T *b, size_t b_len );
Take into account that this declaration within the function
T& s=a,l=b;
is also wrong. It is equivalent to the following declarations
T& s=a;
T l=b;
That is the first declaration declares a reference to an array while the second declaration declares an array and tries to initialize it with another array. However arrays do not have a copy constructor and the compiler will issue one more error. And you may not reassign a reference.
You should know that there is standard algorithm std::search declared in header <algorithm> that can do the job you want to do with your function.
It's because array and sub_arr are two different types. array is of type int[5] while sub_arr is of type int[3]. The array dimensions are part of the type.
Either change the function to use two different templates arguments, one for each array, or use pointer (e.g. T*)
There's also another error, that you will continue to have if you keep using arrays and two different template arguments, and that is that you can't change references. Once you have assigned to the variable s and l in the function, those can't be made to reference anything else. The latter assignments of the s and l variables will fail because there you try to assign the arrays to each other, which you can not do, you can only copy arrays.
If you use pointers instead, then this won't be a problem.
I get this error:
error C2229: class 'GenerateRandNum<int [],int>' has an illegal zero-sized array
In my main, I call my random generator function to input into a empty data set
I call the method in my main like so:
//declare small array
const int smallSize = 20;
int smallArray[smallSize];
// call helper function to put random data in small array
GenerateRandNum <int[], int> genData(smallArray, smallSize);
genData.generate();
Header file
template <class T, class B>
class GenerateRandNum
{
public:
T data;
B size;
GenerateRandNum(T list, B length)
{
data = list;
size = length;
}
void generate();
};
File with method definition
template<class T, class B>
void GenerateRandNum<T, B> ::generate()
{
for (B i = 0; i < size; i++)
{
data[0] = 1 + rand() % size;
}
}
Pointers and arrays are not the same in C/C++. They are two very different things. However, arrays decay into pointers. Most notably in function declarations: The declaration
void foo(int array[7]);
is defined to be equivalent to
void foo(int* array);
That said, all the GenerateRandNum constructor gets, is a int* because that's what T = int [] decays to in the function declaration context. The data member of GenerateRandNum, however, is of type int [] (no decay here), which your compiler assumes to be a zero sized array. Consequently, when you try to assign a pointer to the array, your compiler complains.
You have two options to fix this:
You use an std::vector<> instead, as Marco A. suggests.
You declare your GenerateRandNum class as:
template <class T>
class GenerateRandNum {
public:
T* data;
size_t size;
GenerateRandNum(T* list, size_t length) {
data = list;
size = length;
}
void generate();
};
Note:
I have removed the template parameter for the size type: size_t is guaranteed to be suitable for counting anything in memory, so there is absolutely no point in using anything different. Templating this parameter only obfuscates your code.
There are some problems with your approach:
The first array template parameter can't have its dimension deduced from the argument as n.m. noted, you would need to specify it explicitly:
GenerateRandNum<int[20], int>
There no point in doing
data = list
since in your code sample these are two arrays and you can't assign them directly. You can either copy the memory or specialize your routines/template
You should really consider using a vector of integers, e.g.
template <class T, class B>
class GenerateRandNum
{
public:
T data;
B size;
GenerateRandNum(T list, B length) {
data = list;
size = length;
}
void generate();
};
template<class T, class B>
void GenerateRandNum<T, B> ::generate()
{
srand((unsigned int)time(NULL)); // You should initialize with a seed
for (B i = 0; i < size; i++) {
data[i] = 1 + rand() % size; // I believe you wanted data[i] and not data[0]
}
}
int main(){
//declare small array
const int smallSize = 20;
std::vector<int> smallArray(smallSize);
// call helper function to put random data in small array
GenerateRandNum <std::vector<int>, int> genData(smallArray, smallSize);
genData.generate();
}
Example
I fixed two issues in the code above, take a look at the comments.
I have the following code which could not be complied.
using namespace std;
void f(int);
template<typename T1, size_t N>
void array_ini_1d(T1 (&x)[N])
{
for (int i = 0; i < N; i++)
{
x[i] = 0;
}
}
What is the proper way to pass the array if the main is something like below.
int main()
{
int a;
cin >> a;
int n = a / 4;
f(n);
return 0;
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
error: no matching function to call to array_ini_1d..............
The problem is that variable size arrays are not supported by c++, and is only supported as compilers extension. That means, the standard doesn't say what should happen, and you should see if you can find in compiler's documentation, but I doubt that such corner cases are documented.
So, this is the problem :
int arr[n];
The solution is to avoid it, and use something supported by c++, like for example std::vector.
I don't think the compiler can deduce the size of a variable-length array in a template. Also, don't forget to forward declare f before you use it. Variable-length arrays are a GCC extension and you should get a warning regarding their use.
You may declare your function like this:
template <typename A, size_t N> void f(A a[N]) {
for(size_t i = 0; i < N; i++)
cout << a[i];
}
However, the problem is that when you call the function, the compiler won't deduce the template parameters, and you will have to specify them explicitly.
char arr[5] = {'H', 'e', 'l', 'l', 'o'};
int main()
{
//f(arr); //Won't work
f<char, sizeof(arr)/sizeof(arr[0])>(arr);
cout << endl;
return 0;
}
Unfortunately, that ruins the very idea...
UPD: And even that code does NOT work for an array that has variable length, for the length is calculated at runtime, and the template parameters are defined at compilation time.
UPD2: If using std::vector you may create it initialized:
vector<int> arr(n, 0);
Or you may fill it with fill from <algorithm> when needed:
std::fill(arr.begin(), arr.end(), 0);
As you use Variable length array (VLA) (compiler extension), compiler cannot deduce N.
You have to pass it by pointer and give the size:
template<typename T>
void array_ini_1d(T* a, std::size_t n)
{
for (std::size_t i = 0; i != n; ++i) {
a[i] = 0;
}
}
void f(int n)
{
int arr[n];
array_ini_1d(arr);
}
Or use std::vector. (no extension used so). Which seems cleaner:
template<typename T>
void array_ini_1d(std::vector<T>& v)
{
for (std::size_t i = 0, size = v.size(); i != n; ++i) {
a[i] = 0; // or other stuff.
}
}
void f(int n)
{
std::vector<int> arr(n); // or arr(n, 0).
array_ini_1d(arr);
}
Template parameters must be resolved at compile-time.
There is no way that a function template with parameter size_t N can match any sort of array or other container whose size comes from a run-time input.
You will need to provide another version of the array_1d_ini which does not have the size as a template parameter.
template<typename T, size_t N>
void f(T* a)
{
/* add your code here */
}
int main()
{
int a[10];
f<int, 10>(a);
return 0;
}