Array size deduction - c++

Following an example from Scott Meyer's "Modern C++", I'd exploit std::array size deduction with templates. I'm stumbled trying to compile my myUsage function usage.
#include <array>
#include <iostream>
template <typename T, std::size_t N>
constexpr std::size_t arraySize(T (&) [N]) noexcept
{
return N;
}
void scottUsage()
{
int b[5];
std::array<short, arraySize(b)> c;
std::cout << arraySize(b) << " = " << c.size() << "\n";
}
template <typename T, std::size_t N>
void myUsage(T & arr [N])
{
for (auto i=0; i<arraySize(arr); i++)
std::cout << arr[i] << "\t";
}
int main()
{
scottUsage();
int a[7];
myUsage(a);
}
So two questions arise:
(side question) What's (&) for? Removing would trigger error: creating array of references, which it seems to be forbidden
What's wrong with myUsage signature?

What's (&) for?
It makes the argument a reference. If it wasn't a reference, then it would be an array. But function arguments are not allowed to be arrays, and such declarations are adjusted to be a pointer to an element of the array. Since this type does not carry information about size of the array, it is not useful to the size deduction.
Removing would trigger error: creating array of references, which it seems to be forbidden
void myUsage(T & arr [N])
Indeed, the parentheses are mandatory. They are needed to signify that the reference applies to the array and not the element type of the array. T&[N] is an array of references (which is not allowed) while T(&)[N] is a reference to an array.
What's wrong with myUsage signature?
Arrays of references are not allowed.

myUsage() needs to use T (&arr) [N].
template <typename T, std::size_t N>
void myUsage(T (&arr) [N])
The parentheses tell the compiler that the reference applies to the array itself and not to the element type of the array.
Also, both arraySize() and myUsage() should take a reference to const data:
template <typename T, std::size_t N>
constexpr std::size_t arraySize(const T (&) [N]) noexcept
...
template <typename T, std::size_t N>
void myUsage(const T (&arr) [N])
BTW, arraySize() is not needed from C++17 onward, use std::size() instead:
#include <array>
#include <iostream>
#include <iterator>
void scottUsage()
{
int b[5];
std::array<short, std::size(b)> c;
std::cout << std::size(b) << " = " << c.size() << "\n";
}
template <typename T, std::size_t N>
void myUsage(const T (&arr) [N])
{
for (auto i = 0; i < std::size(arr); ++i)
std::cout << arr[i] << "\t";
/* better:
for (const T &value : arr)
std::cout << value << "\t";
*/
}
int main()
{
scottUsage();
int a[7];
myUsage(a);
}

Related

get size of std::array and span generically at compile time

I searched a fair amount of time in the standard library but just can't find the name of the template function calculating the element count of a std::array type or std::span type or c-array type. What I mean is something similar to get_element_count in the example code below.
I am not looking for a solution, get_element_count works fine. I just believed there exists a standard function already.
The function must take a type as a parameter like std::tuple_size for e.g. but needs to be more generic.
So the Question is: What is the name of that function in the standard library? I am happy with the answer from #user17732522 which is that I cannot find it because it does not exist.
#include <iostream>
#include <array>
#include <span>
#include <type_traits>
template<typename>
inline constexpr size_t get_element_count = 0;
template<typename T, size_t N>
inline constexpr size_t get_element_count<std::array<T, N>> = N;
template<typename T, size_t N>
inline constexpr size_t get_element_count<std::span<T, N>> = N;
template<typename T, size_t N>
inline constexpr size_t get_element_count<T[N]> = N;
int main ()
{
std::array<char, 7> a = {'0','1','2','3','4','5','6'};
std::span b = a;
std::span<char, 7> c = a;
char d[7] = {'0','1','2','3','4','5','6'};
long e[7] = { 0L, 1L, 2L, 3L, 4L, 5L, 6L};
std::cout << get_element_count<decltype(a)> << std::endl;
std::cout << get_element_count<decltype(b)> << std::endl;
std::cout << get_element_count<decltype(c)> << std::endl;
std::cout << get_element_count<decltype(d)> << std::endl;
std::cout << get_element_count<decltype(e)> << std::endl;
}
And because some asked for an example where such a compile time function is needed and not only used:
template <typename T, size_t N>
struct S {T t; size_t n = N;};
using A = std::array<int, 5>;
using B = std::span<int, 5>;
template <typename T>
using C = S<typename T::value_type, 2 * get_element_count<T>>;
using D_double = C<A>;
using E_double = C<B>;

Partial template specialization using STL

I have a function which has template class object as parameter. Its working fine with user defined template class object as paramerter but its giving arror with std::array object. If I am trying to execute this program
#include<iostream>
#include<array>
template<typename T1, int size>
void print(std::array<T1, size> &arr)
{
for (auto ele : arr)
std::cout << ele << " ";
}
template<int size>
void print(std::array<float, size> &arr)
{
std::cout << "its float" << std::endl;
for (auto ele : arr)
std::cout << ele << " ";
}
int main()
{
std::array<int, 3> arr{1,3,4};
std::array<float, 2> ar2{1.3, 3.4};
print(arr);
print(ar2);
return 0;
}
And getting this error
error: no matching function for call to ‘print(std::array<int, 3>&)’
print(arr);
But when I am running this program then its working fine.
#include<iostream>
#include<array>
template<class T, int size>
class Template
{
public:
std::array<T, size> arr;
};
template<typename T1, int size>
void print(Template<T1, size> &obj)
{
for (auto ele : obj.arr)
std::cout << ele << " ";
}
template<int size>
void print(Template<float, size> &obj)
{
for (auto ele : obj.arr)
std::cout << ele << " ";
}
int main()
{
Template<int, 3> array;
array.arr[0] = 1;
array.arr[1] = 2;
array.arr[2] = 3;
Template<float, 2> arr2;
arr2.arr[0] = 2.3;
arr2.arr[1] = 2.9;
print(array);
print(arr2);
return 0;
}
I don't understand why std::array<T1, size> is not suitable match for std::array<int, 3> but class Template<T1,size> is suitable match for Template<int, 3>
Because the 2nd template parameter's type of std::array is std::size_t, but not int; they don't match and cause template argument deduction on the non-type template parameter fails.
(emphasis mine)
If a non-type template parameter is used in the parameter list, and the corresponding template argument is deduced, the type of the deduced template argument ( as specified in its enclosing template parameter list, meaning references are preserved) must match the type of the non-type template parameter exactly, except that cv-qualifiers are dropped, and except where the template argument is deduced from an array bound—in that case any integral type is allowed, even bool though it would always become true:
Change the type to std::size_t.
template<typename T1, std::size_t size>
void print(std::array<T1, size> &arr)
{
for (auto ele : arr)
std::cout << ele << " ";
}
template<std::size_t size>
void print(std::array<float, size> &arr)
{
std::cout << "its float" << std::endl;
for (auto ele : arr)
std::cout << ele << " ";
}
LIVE
You were close. Use size_t instead of int. This compiles:
#include <iostream>
#include <array>
template<typename T1, size_t size>
void print(std::array<T1, size> const &arr)
{
for (auto ele : arr)
std::cout << ele << " ";
}
template<size_t size>
void print(std::array<float, size> &arr)
{
std::cout << "its float" << std::endl;
for (auto ele : arr)
std::cout << ele << " ";
}
int main()
{
std::array<int, 3> arr{1,3,4};
std::array<float, 2> ar2{1.3, 3.4};
print(arr);
print(ar2);
return 0;
}

C++ template for all pointers and template for all arrays

I'm looking for a solution for the following problem: I have a class in which I want to overload an operator (in this example &) for all types of pointers and for all types of arrays. Inside the implementation for arrays I need to have access to the arraysize and inside the implementation for pointers I must be able to do something with the dereferenced object.
As pointed out here, the way for the arrays is quite clear:
template<typename T, unsigned int N>
void operator&(T (&arr)[N])
{
cout << "general array operator: " << N << "\r\n";
}
But for the pointers neither of the following works:
// if I use this, the operator gets ambigous for arrays
template<typename T>
inline void operator&(T* p)
{
cout << "general pointer operator: " << (*p) << "\r\n";
}
// this doesn't work because one cannot dereference void*
void operator&(void* p)
{
cout << "general pointer operator\r\n";
(*this) & (*p);
}
Is there any good and clean solution to achieve different behaviour of an operator for arbitrary arrays and arbitrary pointers?
Here is a complete example code:
#include <iostream>
struct Class
{
template<typename T>
void operator&(T* p)
{
std::cout << "general pointer operator" << (*p) << std::endl;
}
template<typename T, unsigned int N>
void operator&(T (&arr)[N])
{
std::cout << "general array operator" << N << std::endl;
}
};
int main()
{
int myarr[5];
int* p = myarr;
Class obj;
obj & myarr; // error: operator is ambigous
obj & p; // works
return 0;
}
I have to admit that I have no idea why your snippet fails to compile properly. Anyway, a good old tag dispatching workaround seems to be working.
class cClass
{
public:
template<class T, size_t N>
void impl(T (&x)[N], std::true_type)
{
cout << "general array operator" << N << '\n';
}
template<typename T>
void impl(T* p, std::false_type)
{
cout << "general pointer operator" << (*p) << '\n';
}
template<typename T>
void operator&(T && x)
{
impl( std::forward<T>(x), std::is_array< typename std::remove_reference<T>::type >() );
}
};
The solution that changes the least code is:
template<typename T>
void operator&(T*const& p)
which gets rid of the ambiguity. I'd go with tag dispatching myself.
A C++98 solution is to have the pointer-taking operator take a const reference to a pointer.
#include <iostream>
struct Class
{
template<typename T>
void operator&(T* const &p)
{
std::cout << "general pointer operator " << (*p) << std::endl;
}
template<typename T, unsigned int N>
void operator&(T (&)[N])
{
std::cout << "general array operator " << N << std::endl;
}
};
int main()
{
int myarr[1] = { 2 };
int* p = myarr;
Class obj;
obj & myarr;
obj & p;
return 0;
}
Output:
general array operator 1
general pointer operator 2

C++ syntax pointer for array

In the following:
int c[10] = {1,2,3,4,5,6,7,8,9,0};
printArray(c, 10);
template< typename T >
void printArray(const T * const array, int count)
{
for(int i=0; i< count; i++)
cout << array[i] << " ";
}
I am a little confused why the function signature of the template function makes no reference to array being an array by using [], so something like const T * const[] array.
How could one tell from the template function signature that an array is being passed and not just a non-array variable??
You cannot tell for sure. You would have to read the documentation and/or figure it out from the names of the function parameters. But since you are dealing with fixed sized arrays, you could have coded it like this:
#include <cstddef> // for std::size_t
template< typename T, std::size_t N >
void printArray(const T (&array)[N])
{
for(std::size_t i=0; i< N; i++)
cout << array[i] << " ";
}
int main()
{
int c[] = {1,2,3,4,5,6,7,8,9,0}; // size is inferred from initializer list
printArray(c);
}
An array has a size. To create a reference to an array, you need to provide the size statically. For example:
template <typename T, std::size_t Size>
void printArray(T const (&array)[Size]) {
...
}
This functions takes the array by reference and you can determine its size.
You could try something like the following:
template< std::size_t N>
struct ArrayType
{
typedef int IntType[N];
};
ArrayType<10>::IntType content = {1,2,3,4,5,6,7,8,9,0};
template< std::size_t N >
void printArray(const typename ArrayType<N>::IntType & array)
{
//for from 0 to N with an array
}
void printArray(const int * & array)
{
//not an array
}
Raxvan.

Are arrays covariant in their size?

Is there a way to use the new std::array type polymorphically in the size of the array? That is, if I have a function of the form
void DoSomething(std::array<int, 5>& myArray) {
/* ... */
}
Then is it mathematically well-defined to do the following (even if it's not legal C++ code?)
std::array<int, 10> arr;
DoSomething(arr);
Imof this is mathematically well-defined, is there a way to write std::array such that its array elements are contiguous and this code compiles? The only technique I could think of would be to have some weird template metaprogram where std::array<T, N+1> inherits from std::array<T, N>, but I don't believe that forces the array elements to be contiguous.
Directly? No.
You can, however, use compile-time polymorphism to achieve something very similar, and you can write a reference wrapper that makes it easier to work with in the code:
#include <array>
#include <cstddef>
template <typename T, std::size_t N>
struct ref_array_of_at_least
{
template <std::size_t M>
ref_array_of_at_least(T (&a)[M])
: data_(a)
{
static_assert(M >= N, "Invalid size");
}
template <std::size_t M>
ref_array_of_at_least(std::array<T, M>& a)
: data_(&a[0])
{
static_assert(M >= N, "Invalid size");
}
T* data_;
};
Used as:
void f(ref_array_of_at_least<int, 5>) { }
int main()
{
std::array<int, 5> x;
std::array<int, 6> y;
std::array<int, 4> z;
f(x); // ok
f(y); // ok
f(z); // fail
}
(You'd need to add some operator[] overloads and such to ref_array_of_at_least, and it needs some work to make it const correct, but it's a start that demonstrates the possibility of what you are seeking.)
If this was a requirement, one approach is a conversion operator to the required type:
#include <iostream>
template <typename T, int N>
struct Array
{
Array() { for (int i = 0; i < N; ++i) x[i] = 0; }
template <int N2>
operator Array<T, N2>&()
{
// for safety, static assert that N2 < N...
return reinterpret_cast<Array<T, N2>&>(*this);
}
int size() const { return N; }
T x[N];
friend std::ostream& operator<<(std::ostream& os, const Array& a)
{
os << "[ ";
for (int i = 0; i < N; ++i) os << a.x[i] << ' ';
return os << ']';
}
};
void f(Array<int, 5>& a)
{
a.x[a.size() - 1] = -1;
}
int main()
{
Array<int, 10> a;
std::cout << a << '\n';
f(a);
std::cout << a << '\n';
}
I wouldn't recommend it though: pretty horrid. A more explicit mechanism seems a lot less prone to misuse, as well as being more powerful - something vaguely like:
template <size_t N2>
Array<T,N2>& slice(size_t first_index)
{
return *(Array<T,N2>*)(data() + first_index);
}
// usage...
f(a.slice<5>(3)); // elements 3,4,5,6,7.
(clean up the casting for extra points :-/)
No, but you can fake it:
// Hide this function however you like: "detail" namespace, use "_detail"
// in the name, etc.; since it's not part of the public interface.
void f_detail(int size, int *data) {
use(data, /* up to */ data + size);
}
int const f_min_len = 5;
template<int N>
void f(int (&data)[N]) {
static_assert(N >= f_min_len);
f_detail(N, data);
}
template<int N>
void f(std::array<int, N> &data) {
static_assert(N >= f_min_len);
f_detail(N, &data[0]);
}
This is a complete example, and should work exactly as presented. You'd only have to change the data type from int (or make it a template parameter) and add const as required.