Playing with templates, I implemented this one for iterating arrays:
template<class T, size_t size>
void iterate(T(&arr)[size], void(*fn)(T&)) {
for(size_t i=0; i<size; ++i) fn(arr[i]);
}
So I could iterate arrays like this
void printn(int& n) {
printf("%d\n",n);
}
int a[] = {3,-4,6,2};
iterate(a,printn);
Then I realized that I also need const iterator, so the first bad idea was to code
template<class T, class U, size_t size>
void iterate(T(&arr)[size], void(*fn)(U&)) {
for(size_t i=0; i<size; ++i) fn(arr[i]);
}
I know it is bad, but I wonder why this behavior:
void printn(int& n); // works
void printn(const int& n); // also works
void printn(short& n); // type mismatch
void printn(const short& n); // works
There is also an issue with lambdas:
iterate(a,[](int& n) { printf("%d\n",n); }); // type mismatch
So the question is Why the type mismatches and how to solve them?
I use 32bit MinGW 4.8.1 with -std=C++11 option.
Related
My problem is when using sizeof(), the result is the pointer size not the array pointed to.
Please, help me.
Here is the code.
The first function creates a 2d matrix and the second displays it while the main function performs a test.
#include<iostream>
template<typename T>
T** Mat(T vals[],size_t m, size_t n){
T** mat=new T*[m];
for(size_t i=0;i<m;i++) mat[i]=new T[n];
for(size_t mi=0;mi<m;mi++){
for(size_t ni=0;ni<n;ni++){
mat[mi][ni]=vals[mi*(n-1)+ni];
}
}
return mat;
}
template<typename T>
void dispMat2d(T** mat){
for(size_t mi{};mi<sizeof(*mat)/sizeof(mat[0]);mi++){
std::cout<<"I'm here "<<sizeof(*mat)/*/sizeof(mat[0])*/<<"\n";
for(size_t ni{};ni<sizeof(*mat[0])/sizeof(mat[0][0]);ni++){
std::cout<<"o"<<mat[mi][ni]<<"\t";
}
std::cout<<std::endl;
}
}
int main(int c, char* arr[]){
double v[]={1.0,2,3,4,5,6};
auto myMat=Mat<double>(v,2,3);
dispMat2d(myMat);
return 0;
}
I tried to use std::size() but it doesn't accept the arguments *myMat1 and **myMat1.
Thanks in advance
Pointers do not keep the information whether they point to a single object or first element of an array.
Compare
int *p = new int();
and
int *p = new int[10]();
So sizeof( *p ) in the both cases is equivalent to sizeof( int ).
So you have to pass sizes of arrays explicitly.
Pay attention to that the function Mat has a bug.
Instead of
mat[mi][ni]=vals[mi*(n-1)+ni];
there must be
mat[mi][ni]=vals[mi * n + ni];
Arrays decay into pointers when passed as arguments to functions, so the size information is lost.
One way to make it work is to take the argument as a reference to an array of a specific size.
Example:
#include<array>
#include<iostream>
// helper
template<typename T, size_t Y, size_t X>
using mat_t = std::array<std::array<T, X>, Y>;
template<typename T, size_t Y, size_t X, size_t N>
auto Mat(const T (&vals)[N]) {
static_assert(Y*X<=N, "vals[] too small"); // compile time assertion
mat_t<T, Y, X> mat;
for(size_t mi=0; mi<Y; ++mi) {
for(size_t ni=0; ni<X; ++ni) {
mat[mi][ni] = vals[mi*X+ni]; // bugfix
}
}
return mat;
}
template<typename T, size_t Y, size_t X>
void dispMat2d(const mat_t<T, Y, X>& mat){
for(size_t mi=0; mi<Y; ++mi){
for(size_t ni=0; ni<X; ++ni) {
std::cout << 'o' <<mat[mi][ni] << '\t';
}
std::cout << '\n';
}
}
int main(){
double v[]={1,2,3,4,5,6};
auto myMat=Mat<double,2,3>(v);
dispMat2d(myMat);
}
No matching function for call to 'bubbleSort'.
I have this two function in the same .hpp file.
template<typename T>
void bubbleSort(std::vector<T> &vec){
T zacasen;
for (int i=0; i<vec.size(); i++) {
for (int j=0; j<vec.size()-1; j++) {
if(vec[j]>vec[j+1]){
zacasen=vec[j];
vec[j]=vec[j+1];
vec[j+1]=zacasen;
}
}
}
}
template < int N, typename T >
void sort(const std::vector<T> &vec){
if(N==1){
bubbleSort(vec);
}else if(N==2){
//quicksort(vec, 0, vec.size());
}
}
You have
void bubbleSort(std::vector<T> &vec)
but
void sort(const std::vector<T> &vec)
You can't call bubbleSort(vec) from within your sort because a const std::vector<T> is not an std::vector<T> (note the const) and there's no implicit conversion sequence that could turn it into one. Consequently, there's no viable function which overload resolution could pick hereā¦
Why would your sort ask for a const vector to begin with? Since it doesn't return a new vector, the only way it could possibly do what its name suggests it would be doing is by reordering (i.e., modifying) the contents of the vector its given!?
I know how to pass an array of constant size as a reference, but I want to know that how to pass an array of variable size as a reference to another function. Any help would be much appreciated. Thank you
For example, I have the following code snippet:
void y(int (&arr)[n]) //Gives error
{}
void x(Node * tree, int n)
{
int arr[n];
y(arr);
}
I heard that we can templateize the function and make the size a template parameter but I am unable to do so.
Simple: don't. Use std::array or std::vector instead:
int get_max(std::vector<int> & vec) {//Could use const& instead, if it doesn't need to be modified
int max = std::numeric_limits<int>::min();
for(int & val : vec) {if(max < val) max = val;
return max;
}
int get_max(std::array<int, 20> & arr) {//Could use const& instead
int max = std::numeric_limits<int>::min();
for(int & val : arr) {if(max < val) max = val;
return max;
}
If you want this to work for any std::array or any std::vector, you can template them like so:
template<typename T>
T get_max(std::vector<T> const& vec) {
if(vec.size() == 0) throw std::runtime_error("Vector is empty!");
T const* max = &vec[0];
for(T const& val : vec) if(*max < val) max = &val;
return *max;
}
template<typename T, size_t N>
T get_max(std::array<T, N> const& arr) {
static_assert(N > 0, "Array is empty!");
T * max = &arr[0];
for(T & val : arr) if(*max < val) max = &val;
return *max;
}
Your code should now look like this to compensate:
void y(std::vector<int> & arr) //Can be const& if you don't need to modify it.
{}
void x(Node * tree, int n)
{
std::vector<int> arr(n); //Will initialize n elements to all be 0.
y(arr);
}
This answer is to illustrate how to work with VLA in C++ when passing it as a function parameter.
In c99, the syntax allows you to pass the size of the array as a parameter to the function, and use the function parameter to declare the size of the VLA:
void y (int n, int (*arr)[n])
{}
void x (int n)
{
int arr[n];
y(n, &arr);
}
C++ uses "function name mangling" as a technique to encode the parameter types accepted by the function into the function name to support function overloading. However, in GCC, since VLA is not a C++ supported feature, there is no mangling convention for it. One could argue this is a G++ bug (or incomplete support of the VLA extension), but it is what it is. To mimic the pass by reference, accept the decayed pointer as the parameter, and cast it to a reference to the VLA.
void y(int n, int *x)
{
int (&arr)[n] = reinterpret_cast<int (&)[n]>(*x);
}
void x(int n)
{
int arr[n];
y(n, arr);
}
I have verified this works for GCC 4.8.
Xirema already mentioned how to resolve this using std::vector/std::array.
I don't know your exact case, so will just describe another options, despite the fact, that std::vector/std::array is the best.
Pointers option
Here you have believe, that arr and n arguments of y are consistent. And handle arr size manually.
void y(int * arr, const int n) {}
void x(Node * tree, int n) {
int arr[n];
y(arr, n);
}
Templates option
This will work, howether it will instantiate 2 templates on each new N value.
template <size_t N>
void y(int (&arr)[N]) {}
template <size_t N>
void x(Node * tree) {
int arr[N];
y<N>(arr);
}
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;
}
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;
}