Why does the std::size() do not work on a statically allocated array passed by value?
void print_elemTab(int tab[])
{
// ...
int size = std::size(tab); //error
// ...
}
void test_tab()
{
const int TAB_SIZE = 5;
int tab[TAB_SIZE] = {};
// ...
cout << std::size(tab) << std::endl; //print 5
print_elemTab(tab);
// ...
}
I am printing the size and then I am passing the tab in the sub-function print_elemTab() where I am using std::size() again.
I get no matching function error, so I wonder why std::size() works the first time in test_tab() and not in print_elemTab()
Do I have to pass it by reference? And so how do I make it but for an array of any length?
Or do I have to make it in another way because of something that I am not aware of?
Array designators used in expressions are implicitly converted (with rare exceptions as for example using them in the sizeof operator) to pointers to their first elements.
So in this call
print_elemTab(tab);
the argument expression has the type int *.
On the other hand, a function parameter having an array type is adjusted by the compiler to pointer to the element type of the array.
So for example these function declarations
void print_elemTab(int tab[]);
void print_elemTab(int tab[5]);
void print_elemTab(int tab[100]);
declare the same one function and are equivalent to the following declaration
void print_elemTab(int *tab);
You nay even include all these declarations in your program though the compiler can issue a message that there are redundant declarations.
Hence within the function you are dealing with a pointer of the type int *. And sizeof( int * ) is usually equal to 4 or 8 depending on the used system.
If you have such a declaration you should change it specifying a second parameter that will keep the number of elements in the passed array like
void print_elemTab(int *tab, size_t n );
And the function can be called like
print_elemTab(tab, std::size( tab ) );
Another approach is to pass the array by reference. In this case you should declare a template function for example like
template <size_t N>
void print_elemTab( int ( &tab )[N] );
Within the function you can either directly use the template parameter N as the number of elements in the array. Or you can apply the same standard C++ function std::size to the array.
Or the function can be even more general declared with a second template type parameter like
template <typename T, size_t N>
void print_elemTab( T ( &tab )[N] );
Another approach is to declare the function like
template <typename Container>
void print_elemTab( Container &container );
In this case you also may apply the standard function std::size to the parameter container.
Do I have to pass it by reference? And so how do I make it but for any
array of any length?
Yes, passing it by reference would be one option.
template<std::size_t n>
void print_elemTab(int (&tab)[N]) // const int (&tab)[N], if the elements won't be modified
{
std::cout << N << "\n"; // where you can directly get the size `N`
}
Or like simple templated function as follows
template<typename T>
void print_elemTab(T& tab)// const T& tab, if the elements won't be modified
{
const auto size = std::size(tab);
std::cout << size << "\n";
}
Another option is to deduce the array to its actual type. In your case, the tab has the type int[5]. The compiler can deduce to its actual type(other than decaying to pointer) if you perfectly forward via a template function.
#include <iostream>
#include <array>
template<typename T>
void print_elemTab(T&& tab)
{
const auto size = std::size(tab); // now you can do std::size() on the int[size]
std::cout << size << "\n";
}
Yes, you have to pass it by reference because it's decayed to a pointer on passing it to your function. and to make the function accept any size I suggest to use a function template as follows
#include <iostream>
template<size_t n>
void print_elemTab(int (&tab)[n])
{
int size = std::size(tab);
std::cout << size << "\n";// or just std::cout << n; and ignore the previous line
}
void test_tab() {
const int TAB_SIZE = 5;
int tab[TAB_SIZE] = {};
std::cout << std::size(tab) << std::endl; //print 5
print_elemTab(tab);
}
int main(){
test_tab();
}
Related
I want to design a function that can return the value of an array easily. But I meet some problems. In the return statement of function getArrayLen(), the value of sizeof(array) seems to be 4, which is obviously wrong. But if I use sizeof(people) in the main function, it returns 300, which is right. I wonder why? I konw that sizeof(array) returns 4 because "array" is a pointer, but isn't "people" in sizeof(people) a pointer, too?
#include <iostream>
#include <string>
using namespace std;
template <class T>
int getArrayLen(T& array)
{
cout << "size of array: " << sizeof(array) << endl;
cout << "size of array[0]: " << sizeof(array[0]);
return (sizeof(array) / sizeof(array[0]));
}
struct Person
{
string name;
int age;
string sex;
};
void test(Person arr[])
{
int length = getArrayLen(arr);
}
int main()
{
Person people[5] =
{
{"John",23,"Male"},
{"Alan",22,"Male"},
{"Alex",20,"Male"},
{"James",21,"Male"},
{"Lucy",19,"Male"}
};
test(people);
return 0;
}
How can I design a function that can return the length of an array easily?
You could use a function template that deduces the size from an array argument. However, there is no need to design such template, because standard library provides one already: std::size.
the value of sizeof(array) seems to be 4, which is obviously wrong. But if I use sizeof(people) in the main function, it returns 300, which is right. I wonder why?
That is because array, despite its name, and despite what the declaration looks like, is not an array. A function parameter is never an array.
The parameter is a pointer which happens to an element of an array. Your function cannot be used to get the size of an array using a pointer to an element of the array.
but isn't "people" in sizeof(people) a pointer, too?
No. people is not a pointer. It is an array.
Just fix the functions: getArrayLen & test to general function:
template <class T>
int getArrayLen(const T& array) {
return sizeof(T);
}
template <class T>
void test(const T& array)
{
int length = getArrayLen(array);
cout << length << endl;
}
Result:
300
Why does the std::size() do not work on a statically allocated array passed by value?
void print_elemTab(int tab[])
{
// ...
int size = std::size(tab); //error
// ...
}
void test_tab()
{
const int TAB_SIZE = 5;
int tab[TAB_SIZE] = {};
// ...
cout << std::size(tab) << std::endl; //print 5
print_elemTab(tab);
// ...
}
I am printing the size and then I am passing the tab in the sub-function print_elemTab() where I am using std::size() again.
I get no matching function error, so I wonder why std::size() works the first time in test_tab() and not in print_elemTab()
Do I have to pass it by reference? And so how do I make it but for an array of any length?
Or do I have to make it in another way because of something that I am not aware of?
Array designators used in expressions are implicitly converted (with rare exceptions as for example using them in the sizeof operator) to pointers to their first elements.
So in this call
print_elemTab(tab);
the argument expression has the type int *.
On the other hand, a function parameter having an array type is adjusted by the compiler to pointer to the element type of the array.
So for example these function declarations
void print_elemTab(int tab[]);
void print_elemTab(int tab[5]);
void print_elemTab(int tab[100]);
declare the same one function and are equivalent to the following declaration
void print_elemTab(int *tab);
You nay even include all these declarations in your program though the compiler can issue a message that there are redundant declarations.
Hence within the function you are dealing with a pointer of the type int *. And sizeof( int * ) is usually equal to 4 or 8 depending on the used system.
If you have such a declaration you should change it specifying a second parameter that will keep the number of elements in the passed array like
void print_elemTab(int *tab, size_t n );
And the function can be called like
print_elemTab(tab, std::size( tab ) );
Another approach is to pass the array by reference. In this case you should declare a template function for example like
template <size_t N>
void print_elemTab( int ( &tab )[N] );
Within the function you can either directly use the template parameter N as the number of elements in the array. Or you can apply the same standard C++ function std::size to the array.
Or the function can be even more general declared with a second template type parameter like
template <typename T, size_t N>
void print_elemTab( T ( &tab )[N] );
Another approach is to declare the function like
template <typename Container>
void print_elemTab( Container &container );
In this case you also may apply the standard function std::size to the parameter container.
Do I have to pass it by reference? And so how do I make it but for any
array of any length?
Yes, passing it by reference would be one option.
template<std::size_t n>
void print_elemTab(int (&tab)[N]) // const int (&tab)[N], if the elements won't be modified
{
std::cout << N << "\n"; // where you can directly get the size `N`
}
Or like simple templated function as follows
template<typename T>
void print_elemTab(T& tab)// const T& tab, if the elements won't be modified
{
const auto size = std::size(tab);
std::cout << size << "\n";
}
Another option is to deduce the array to its actual type. In your case, the tab has the type int[5]. The compiler can deduce to its actual type(other than decaying to pointer) if you perfectly forward via a template function.
#include <iostream>
#include <array>
template<typename T>
void print_elemTab(T&& tab)
{
const auto size = std::size(tab); // now you can do std::size() on the int[size]
std::cout << size << "\n";
}
Yes, you have to pass it by reference because it's decayed to a pointer on passing it to your function. and to make the function accept any size I suggest to use a function template as follows
#include <iostream>
template<size_t n>
void print_elemTab(int (&tab)[n])
{
int size = std::size(tab);
std::cout << size << "\n";// or just std::cout << n; and ignore the previous line
}
void test_tab() {
const int TAB_SIZE = 5;
int tab[TAB_SIZE] = {};
std::cout << std::size(tab) << std::endl; //print 5
print_elemTab(tab);
}
int main(){
test_tab();
}
This question already has an answer here:
Why does the sizeof operator return the wrong value when used on an array passed to a method? [duplicate]
(1 answer)
Closed 2 years ago.
I created an array of ten integers and print its size
1. in main
2. in a function named print
#include <iostream>
using namespace std;
void print(int *a)
{
cout<<sizeof(a);
}
int main()
{
int arr[10];
cout<<sizeof(arr)<<endl;
print(arr);
}
The output is :
40
8
I was expecting 40 in both cases (as size of 1 integer=4 times 10 ) but the second case shows size of a single pointer and not the whole array. What's happening here?
As you can see from the function declaration
void print(int *a)
{
cout<<sizeof(a);
}
the function deals with a pointer. So this statement
cout<<sizeof(a);
outputs the size of the pointer that is equal either 4 or 8 bytes depending on the used system.
Pay attention to that even if you will declare the function like
void print(int a[])
{
cout<<sizeof(a);
}
you will get the same result because the parameter is implicitly adjusted by the compiler to the type int *.
That is the previous and this function declarations are equivalent.
If you want that the function would deal with the original array instead of a pointer that points to the first element of the passed array as an argument then declare the function at least like.
void print( int ( &a )[10] )
{
cout<<sizeof(a);
}
That is declare the parameter as a reference.
Or you can make the function a template function like
template <size_t N>
void print( int ( &a )[N] )
{
cout<<sizeof(a);
}
As the passed array is not being changed in the function then the parameter should have the qualifier const
template <size_t N>
void print( const int ( &a )[N] )
{
cout<<sizeof(a);
}
Here is a demonstrative program.
#include <iostream>
template <size_t N>
void print( const int ( &a )[N] )
{
std::cout << sizeof( a ) << '\n';
}
int main()
{
int arr[10];
std::cout << sizeof( arr ) << '\n';
print( arr );
return 0;
}
Its output is
40
40
Or you could define the template function with a type template parameter. But again the function parameter has a referenced type.
#include <iostream>
template <typename T>
void print( const T &a )
{
std::cout << sizeof( a ) << '\n';
}
int main()
{
int arr[10];
std::cout << sizeof( arr ) << '\n';
print( arr );
return 0;
}
The program output will be the same as shown above.
When you pass an array to a function, it gets adjusted into a pointer to its first element inside the function.
So what you see in print function isn't the size of the array but the size of the pointer i.e. sizeof(a) is equivalent to sizeof(int*).
you should see the output for this code, and observe the difference:
#include <iostream>
using namespace std;
int* h()
{
int *a[10];
cout<<sizeof(a);
return a;
}
int main()
{
int *a=h();
cout<<sizeof(a);
}
Output :
40
8
now main shows size as 8 (size of pointer) and function print shows size of array as 40.
I'm trying to implement a C++ program that, given a list of elements, prints out unique elements from the list.
I know C a lot better than C++, but I am starting out on C++ practically (coding) only now.
I've only read up on C++ concepts what templates are and I'm comfortable with function templates but I just read up on class templates and I think I'm getting confused as to where to use which one, as applied to the scenario below.
Here's what I've written so far (*note that the function isUnique is supposed to be doing something else but I'm writing just verifiable actions inside it for now):
cppArrays.h
#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;
template <class T> class cpparray{
private:
int size;
vector<T> elems;
public:
cpparray(int);
~ cpparray();
int isUnique(T arr);
};
template <class T> cpparray<T>::cpparray(int size)
{
vector<T> elems(size);
cout << "Object created with size " << elems.size()<< "\n"<< endl;
}
template <class T> cpparray<T>::~cpparray()
{
cout << "Object del\n" << endl;
}
template <class T> int cpparray<T>::isUnique(T arr)
{
return arr.size();
}
cppArrays.cc
#include "cppArrays.h"
int main()
{
cpparray<int> a(10) ;
//a.push_back(1);
//a.push_back(2);
//cout << a.size() << a.begin() << a.end() << endl;
int b = isUnique(a);
return 0;
}
Details:
[1] I'm trying to use templates, because I want my vector to be able to be instantiated with any data type - char/float/int.
[2] I realize that in using a class template, by calling
cpparray<int>a(10);
I end up declaring an object a of class "cpparray" whose type is Integer. Is this correct?
If yes, then a.push_back(1) wouldn't work because it doesn't refer to the member variable but the object itself and hence it's understandable I'll get a compile time error saying no member named push_back in cpparray.
But this is making it even more difficult for me to
[1] understand when to use class template as opposed to function template,
[2] How do I initialize this vector in the template class and work with it, given my objective?
Your constructor should use initialization list to init members,
like this:
template <class T> array<T>::array(int sz) : size(sz), elems(sz)
{
cout << "Object created with size " << elems.size()<< "\n"<< endl;
}
What you did is to declare the vector as local in the constructor, init it to size, and the local vector was destroyed at the end of block.
Use a class template when you need a generic type that has compile-time variable properties. Template parameters can be types and constants, e.g.
template<typename T, size_t Size>
class MyArray {
T elements_[Size];
public:
MyArray() {}
// ...
};
Use a function template when wish to write a generic function that can be applied to various types/parameters:
#include <cstdio>
#include <iostream>
template<size_t BufSize, typename... Args>
int strprintf(char(&buf)[BufSize], const char* fmt, Args&&... args)
{
static_assert(BufSize > 0, "Buffer too small");
static_assert(BufSize < (1 << 31), "Buffer too large");
return snprintf(buf, BufSize, fmt, std::forward<Args>(args)...);
}
int main() {
char buf[16];
int printed = strprintf(buf, "hello world %s so long", "oversized load");
std::cout << buf << "\n";
}
http://ideone.com/SLUQX3
The above is an example of how you can replace one of those old vsnprintf forwarding printf-type functions; doing all the legwork at compile time makes it drastically more efficient.
BufSize can be deduced by the compiler because the type of buf is char[16]; it can capture the source by reference, the type is char and the array size - the template variable - is 16.
It's also possible to have a templated member function of a templated class:
template<typename T>
class Foo {
T t_;
public:
Foo() : t_() {}
Foo(const T& t) : t_(t) {}
template<typename RhsT>
bool is_same_size(const RhsT& rhs) {
return t_.size() == rhs.size();
}
};
This example will only work for instances when both T and RhsT have a size() member function, leading to the following:
Foo<vector<int>> fvi;
Foo<list<double>> fld;
fvi.is_same_size(fld); // fine
Foo<int> fi;
fvi.is_same_size(fi); // compiler error
I don't understand why the array decays to a pointer in a template function.
If you look at the following code: When the parameter is forced to be a reference (function f1) it does not decay. In the other function f it decays. Why is the type of T in function f not const char (buff&)[3] but rather const char* (if I understand it correctly)?
#include <iostream>
template <class T>
void f(T buff) {
std::cout << "f:buff size:" << sizeof(buff) << std::endl; //prints 4
}
template <class T>
void f1(T& buff) {
std::cout << "f:buff size:" << sizeof(buff) << std::endl; //prints 3
}
int main(int argc, char *argv[]) {
const char buff[3] = {0,0,0};
std::cout << "buff size:" << sizeof(buff) << std::endl; //prints 3
f(buff);
f1(buff);
return 0;
}
It is because arrays cannot be passed by value to a function. So in order to make it work, the array decays into a pointer which then gets passed to the function by value.
In other words, passing an array by value is akin to initializing an array with another array, but in C++ an array cannot be initialized with another array:
char buff[3] = {0,0,0};
char x[3] = buff; //error
So if an array appears on the right hand side of =, the left hand side has to be either pointer or reference type:
char *y = buff; //ok - pointer
char (&z)[3] = buff; //ok - reference
Demo : http://www.ideone.com/BlfSv
It is exactly for the same reason auto is inferred differently in each case below (note that auto comes with C++11):
auto a = buff; //a is a pointer - a is same as y (above)
std::cout << sizeof(a) << std::endl; //sizeof(a) == sizeof(char*)
auto & b = buff; //b is a reference to the array - b is same as z (above)
std::cout << sizeof(b) << std::endl; //sizeof(b) == sizeof(char[3])
Output:
4 //size of the pointer
3 //size of the array of 3 chars
Demo : http://www.ideone.com/aXcF5
Because arrays can not be passed by value as a function parameter.
When you pass them by value they decay into a pointer.
In this function:
template <class T>
void f(T buff) {
T can not be char (&buff)[3] as this is a reference. The compiler would have tried char (buff)[3] to pass by value but that is not allowed. So to make it work arrays decay to pointers.
Your second function works because here the array is passed by reference:
template <class T>
void f1(T& buff) {
// Here T& => char (&buff)[3]
To quote from spec, it says
(14.8.2.1/2) If P is not a reference type: — If A is an array type,
the pointer type produced by the array-to-pointer standard conversion
(4.2) is used in place of A for type deduction; otherwise
So, in your case, It is clear that,
template <class T>
void f1(T& buff) {
std::cout << "f:buff size:" << sizeof(buff) << std::endl; //prints 3
}
doesn't decay into pointer.
Because functions can't have arrays as arguments. They can have array references though.
The reason basically boils down to type deduction when matching the different overloads. When you call f the compiler deduces the type to be const char[3] which then decays into const char* because that's what arrays do. This is done in the same exact way that in f(1) the compiler deduces T to be int and not int&.
In the case of f1 because the argument is taken by reference, then the compiler again deduces T to be const char[3], but it takes a reference to it.
Nothing really surprising, but rather consistent if it were not for the decay of arrays to pointers when used as function arguments...