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.
Related
I've been trying to write some shorter code that still uses std::array but I'm not sure how to get the deduction to work in my favor.
Is there a way to write this that is equally as short(readable) as the vector version?
#include <array>
#include <iostream>
#include <vector>
template <std::size_t SIZE>
void afunc(const std::array<std::string_view, SIZE> &v)
{
for(int i=0; i<10; i++)
{
std::cout << v[rand()%SIZE];
}
}
void vfunc(const std::vector<std::string_view> &v)
{
for(int i=0; i<10; i++)
{
std::cout << v[rand()%v.size()];
}
}
using namespace std::string_view_literals;
int main()
{
std::array<std::string_view, 2> works = {"1","2"};
afunc(works);// verbose
//afunc({ "1","2" }); // 1st fail
//afunc({ "1"sv,"2"sv }); // 2nd fail
//afunc(std::array{ "3","4" });// 3rd fail
afunc(std::array{ "3"sv,"4"sv, "test"sv,"other"sv });// works but verbose
vfunc({"5","6", "7"});// vectors are easy to code with
return 0;
}
std::array and std::vector have the same size() and operator[] members, so you could roll your 2 functions into 1 function:
template <typename Container>
void func(const Container &c)
{
for(int i = 0; i < 10; ++i)
{
std::cout << c[rand() % c.size()];
}
}
std::array<std::string_view, 2> a{"1","2"};
func(a);
std::vector<std::string_view> v{"1","2"};
func(v);
func<std::array<std::string_view, 2>>({"1","2"});
func<std::vector<std::string_view>>({"1","2"});
Demo
But, as you can see, that can be too verbose for your taste. But, you can support braced lists as a parameter without having to specify the template argument explicitly, by defining an extra function that accepts std::initializer_list as a parameter, and then the compiler can deduce that parameter for you.
But, std::initializer_list doesn't have operator[], so you would have to change your main function logic to use random-access iterators instead, if you intend to share common code between the functions, eg:
template <typename Iter>
void func(Iter begin, Iter end, std::random_access_iterator_tag)
{
size_t size = end - begin;
for(int i = 0; i < 10; ++i)
{
std::cout << *(begin + (std::rand() % size));
}
}
template <typename Iter>
void func(Iter begin, Iter end)
{
func(begin, end,
typename std::iterator_traits<Iter>::iterator_category());
}
template <typename Container>
void func(const Container &c)
{
func(std::begin(c), std::end(c));
}
template <typename T>
void func(const std::initializer_list<T> &l)
{
func(l.begin(), l.end());
}
std::array<std::string_view, 2> a{"1","2"};
func(a);
std::vector<std::string_view> v{"1","2"};
func(v);
func({"1"sv, "2"sv});
Demo
You can't infer a compile-time size from an initializer list parameter, so you need to pass the size explicitly:
template <std::size_t Size>
void afunc(const std::array<std::string_view, Size> &v)
{ ... }
int main()
{
afunc<2>({ "1", "2" }); // ok
}
Your vfunc for std::vector doesn't need to deduce a compile-time size, which is why this limitation only applies to template argument deduction for std::array in afunc.
Found out how to get C style arrays to work as an option. When I had tried this before my reference was not const thus would not compile with a temporary. Not as good as arrays for the lack of iterators but still straightforward
template<size_t N>
void carr_try(std::string_view const(&a)[N])
{
for(int i=0; i<10; i++)
{
std::cout << a[rand()%N];
}
}
// for illustrative purposes of how to NOT write this routine
template<size_t N>
void nonconst(std::string_view (&a)[N])
{
for(int i=0; i<10; i++)
{
std::cout << a[rand()%N];
}
}
int main()
{
carr_try({ "1", "2" }); // ok
//nonconst({"1","2"});// not ok
std::string_view temp[]={"3","4"};
nonconst(temp);// is fine but long
}
Suppose you have a class that operates on a vector:
class Foo{
public:
Foo() {
m_dynamic_data.push_back(5);
std::cout << m_dynamic_data[0] << std::endl;
}
private:
std::vector<int> m_dynamic_data;
};
In my case this class is huge with 2500 additional lines of code.
This class behaves dynamic (hence std::vector). But I would also like to provide a "static" implementation (using std::array). So std::size_t N is added, which now should control when to use which attribute.
template<std::size_t N>
class Foo{
private:
std::vector<int> m_dynamic_data; //use this, when N == 0
std::array<int, N> m_static_data; //use this, when N != 0
};
I am not sure if I can get this to work. using #define won't do the job (since it can't alternate). constexpr can't be wrapped around two attributes either. The best solution is probably to provide a base class and then inherit the dynamic and static case from it. But before I spent the next days doing this, I wonder if there isn't a technique afterall.
I thought about putting both into a std::unique_ptr and only constructing the relevant array:
template<std::size_t N>
class Foo {
public:
Foo() {
if constexpr (N) {
m_static_data_ptr = std::make_unique<std::array<int, N>>();
(*m_static_data_ptr)[0] = 5;
std::cout << (*m_static_data_ptr)[0] << std::endl;
}
else {
m_dynamic_data_ptr = std::make_unique<std::vector<int>>(1);
(*m_dynamic_data_ptr)[0] = 5;
std::cout << (*m_dynamic_data_ptr)[0] << std::endl;
}
}
private:
std::unique_ptr<std::vector<int>> m_dynamic_data_ptr;
std::unique_ptr<std::array<int, N>> m_static_data_ptr;
};
I earlier asked about this case here. But apparently this doesn't seem like a good approach. (fragmenting memory, cache miss rate). std::optional also seems interesting, but it pushes the sizeof(Foo) too far for my goal.
Ultimately there is also using void pointers:
template<std::size_t N>
class Foo {
public:
Foo() {
if constexpr (N) {
m_data = malloc(sizeof(std::array<int, N>));
(*static_cast<std::array<int, N>*>(m_data))[0] = 5;
std::cout << (*static_cast<std::array<int, N>*>(m_data))[0] << std::endl;
}
else {
m_data = new std::vector<int>;
(*static_cast<std::vector<int>*>(m_data)).push_back(5);
std::cout << (*static_cast<std::vector<int>*>(m_data))[0] << std::endl;
}
}
~Foo() {
delete[] m_data;
}
private:
void* m_data;
};
But this seems pretty dirty [...]
So the goal would be to work with either array structure at compile time. Thanks for any help / suggestion!
You can abstract the data part of Foo to another class template.
template<std::size_t N> struct FooData
{
std::array<int, N> container;
}
template <> struct FooData<0>
{
std::vector<int> container;
}
template<std::size_t N>
class Foo{
private:
using DataType = FooData<N>;
DataType data;
};
You have to add member functions to FooData to support additional abstractions. The number of such functions and their interface depends on how differently you use the containers in Foo.
R Sahu's answer is great, but you don't need to access the container indirectly through a struct.
template<std::size_t N>
struct FooData { using type = std::array<int, N>;};
template <>
struct FooData<0> { using type = std::vector<int>; };
template<std::size_t N>
using FooData_t = typename FooData<N>::type;
template<std::size_t N>
class Foo{
private:
FooData_t<N> data;
};
Alternatively, you can also use std::conditional_t:
template<std::size_t N>
class Foo{
private:
std::conditional_t<N==0, std::vector<int>, std::array<int, N>> data;
};
You may want to isolate this dynamic/static "morphing" from the rest of your giant 2500-lines Foo class. I can imagine a tiny wrapper around std::array to mimic the interface of std::vector. It can be used as a member of Foo. If the static capacity is set to the sentinel value 0, then it can be specialized to just derive from a real std::vector:
#include <cassert>
#include <cstddef>
#include <array>
#include <iostream>
#include <vector>
template<class value_type_, std::size_t capacity_>
struct StaticOrDynamic {
using value_type = value_type_;
static constexpr std::size_t capacity = capacity_;
std::array<value_type, capacity> arr_{};
std::size_t size_{0};
constexpr void push_back(const value_type& x) {
assert(size_ < capacity && "must not exceed capacity");
arr_[size_++] = x;
}
constexpr const value_type_& at(std::size_t i) const {
assert(i < size_ && "must be in [0, size)");
return arr_[i];
}
/* other members etc */
};
template<class value_type_>
struct StaticOrDynamic<value_type_, 0>// specialization for dynamic case
: std::vector<value_type_>
{
using std::vector<value_type_>::vector;
};
template<std::size_t capacity_>
struct Foo {
static constexpr std::size_t capacity = capacity_;
StaticOrDynamic<int, capacity> m_data_{};
Foo() {// static version may be constexpr (without debug output)
m_data_.push_back(5);
std::cout << m_data_.at(0) << std::endl;
}
};
int main() {
Foo<5> static_foo{};
Foo<0> dynamic_foo{};
}
A similar behavior (static/dynamic chosen by a template parameter) is offered in, e.g., the Eigen library. I do not know how it is implemented there.
I have a class that contains an array of objects T without a default constructor. Here is an example:
#include <iostream>
struct Param {
int x;
};
struct A {
A(const Param &p) {
std::cout << p.x << std::endl;
}
};
template<class T, int n>
struct Array {
T data[n];
/* n times */
Array(const Param &p) : data{/* T(p), ..., T(p) */} { }
};
int main() {
Param p{42};
Array<A, 3> arr(p);
return 0;
}
I am looking for a way to initialize the data field using a varying size initializer list of elements T(p) containing exactly n elements. I have a feeling that it can be achieved by some construction like std::make_index_sequence, but I did not find anything appropriate.
I suppose you can use a delegating contructor, std::index_sequence and std::make_index_sequence and rewrite Array as follows (Edit: answer improved by SergeyA and Rakete1111; thanks!)
template <typename T, std::size_t N>
struct Array
{
T data[N];
template <std::size_t ... Is>
Array (Param const & p, std::index_sequence<Is...> const &)
: data { ((void)Is, T{p}) ... }
{ }
Array(const Param &p) : Array{p, std::make_index_sequence<N>{}}
{ }
};
Maybe you can make private the new constructor.
Lets assume I want to write struct that has a member constexpr std::array that contains first N fibs, where N is a template argument.
Something like this but with vals being avaliable at compile time:
template <int N>
struct first_n_fibs {
static_assert(N>0);
static const std::array<int, N> vals;
static std::array<int, N> init_fibs(){
std::array<int,N> result;
if (N==1) {
return std::array<int,N>{1};
} else {
result[0]=1;
result[1]=1;
for(int i =2; i<N;++i) {
result[i]=result[i-2]+result[i-1];
}
}
return result;
}
};
template<int N>
const std::array<int, N> first_n_fibs<N>::vals=init_fibs();
int main(){
std::cout << first_n_fibs<2>::vals.back() << std::endl;
std::cout << first_n_fibs<5>::vals.back() << std::endl;
std::cout << first_n_fibs<6>::vals.back() << std::endl;
}
I suspect there is no workaround since std::array constructors are not constexpr, so if somebody knows any workarounds involving C arrays or boost I would be happy with that.
You don't need anything special, constexpr function requirements are very relaxed these days:
#include <iostream>
#include <array>
template <int N> constexpr std::array<int, N> first_n_fibs()
{
std::array<int, N> ret{};
ret[0] = 0;
if (N == 1) return ret;
ret[1] = 1;
for (int i = 2; i < N; i++)
ret[i] = ret[i-2] + ret[i-1];
return ret;
}
int main()
{
constexpr auto a = first_n_fibs<3>();
}
(try it live)
std::array constructors are not constexpr
Apparently it has no user-defined constructors at all, so nothing stops its construction from being constexpr.
You can use immediately called lambda:
struct foo {
static constexpr auto arr = []{
std::array<int, 6> a{};
for (auto& e : a) {
// stuff
}
return a;
}();
};
But as HolyBlackCat said, std::array is constexpr constructible, since it has no defined constructor, only compiler defined ones.
Here's live example at coliru
How can I create an Array class in C++11 which can be used like
Array < int, 2, 3, 4> a, b;
Array < char, 3, 4> d;
Array < short, 2> e;
and access it in a way like
a[2][1][2] = 15;
d[1][2] ='a';
I also need to overload operator as
T &operator[size_t i_1][size_t i_2]...[size_t i_D];
which does not exist. How can I do this?
The simplest way to do this is by nesting std::array:
#include<array>
template<class T, size_t size, size_t... sizes>
struct ArrayImpl {
using type = std::array<typename ArrayImpl<T, sizes...>::type, size>;
};
template<class T, size_t size>
struct ArrayImpl<T, size> {
using type = std::array<T, size>;
};
template<class T, size_t... sizes>
using Array = typename ArrayImpl<T, sizes...>::type;
In this solution Array<char, 3, 4> is the same as std::array<std::array<char, 4>, 3> - array consisting of arrays of smaller dimension.
This also shows how you can implement operator[] for many dimensions. operator[] of your object needs to return object for which operator[] is also defined. In this case it is reference to an array of smaller dimension.
Try this:
#include <iostream>
template <typename T, int N1, int... N2>
class Array
{
public:
Array() {}
~Array() {}
Array<T,N2...>& operator[](int index)
{
return data[index];
}
private:
Array<T,N2...> data[N1];
};
template<typename T, int N>
class Array<T,N>
{
public:
Array() {}
~Array() {}
T& operator[](int index)
{
return data[index];
}
private:
T data[N];
};
int main()
{
Array < int, 2, 3, 4> a, b;
Array < char, 3, 4> d;
Array < short, 2> e;
a[0][1][2] = 15;
d[1][2] = 'a';
std::cout << "a[0][1][2] = " << a[0][1][2] << std::endl;
std::cout << "d[1][2] = " << d[1][2] << std::endl;
return 0;
}
You might also want to throw in range checking and perhaps some iterators to be fancy :)