I want to initialize my array items while avoiding unnecessary instances and copies (similar to this question: initialize std::array without copying/moving elements).
An initializer list does work for a small amount of objects.
Bit I want to do this via a code snippet since my array has several hundreds of items...
How can I do this?
#include <array>
#include <iostream>
class mytype {
public:
int a;
mytype() : a(0) {}
mytype(int a) : a(a) {}
};
int main() {
// explict constructor calls to instantiate objects does work
std::array<mytype, 2> a = { { mytype(10), mytype(20) } };
std::cout << a[0].a; // 10
// I want to do something like this - what does not work of course
std::array<mytype, 2> b = { { for (i = 0, i++, i < 2) mtype(10 * i); } };
}
In c++14:
#include <array>
#include <utility>
#include <cstddef>
template <typename T, std::size_t... Is>
std::array<T, sizeof...(Is)> to_array(std::index_sequence<Is...>)
{
return { T(Is*10)... };
}
template <typename T, std::size_t N>
std::array<T, N> to_array()
{
return to_array<T>(std::make_index_sequence<N>{});
}
int main()
{
std::array<mytype, 10> b(to_array<mytype, 10>());
}
DEMO
This is typically accomplished with a pair of templates:
namespace detail {
template<std::size_t... Idx>
auto make_mytype_array(std::index_sequence<Idx...>) {
return std::array<mytype, sizeof...(Idx)>{{
mytype(10 * Idx)...
}};
}
}
template<std::size_t N>
auto make_mytype_array() {
return detail::make_mytype_array(make_index_sequence<N>{});
}
The above are a pair of utility free functions, but can be folded into the class if need be. If you need it for more than just an expression like 10*i, then a lambda can be passed as another argument (templated to be a general "callable"). With copy elision this will all collapse into direct initialization of the result array object.
Related
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.
I'd like to have a class that gets in it's Ctor unlimited parameters of the same type, and stores them into a vector. It should look like that:
class A(int a, int b, **N time parameter of type T**)
: data(**vector will get N times type T**)
{
}
protected:
vector<T> data;
How should I implement it? Solution could be in c++11/14
I got a few errors such as "parameter packs not expanded with ‘…' ", etc..
This code sample might be useful:
#include <vector>
#include <utility>
template<typename T>
class MyClass {
public:
template<typename ...Args>
MyClass(int a, int b, Args&& ...args) :data{ std::forward<Args>(args)... } {}
private:
std::vector<T> data;
};
int main() {
MyClass<char> sample(1, 2, 'a', 'b');
return 0;
}
[EDIT]: Added std::forward, added missing include for utility
Assuming that T could be anything, even something quite large or non-copyable, we'd want to:
preserve efficiency with perfect forwarding.
check types.
std::initializer_list satisfies 2 but not 1.
Simple variadic template expansion satisfies 1 and not 2.
This solution uses variadic template expansion and enable_if to enforce type compatibility.
#include <vector>
#include <utility>
#include <string>
namespace detail
{
constexpr bool all()
{
return true;
}
template<class...Rest>
constexpr bool all(bool b, Rest...rest)
{
return b and all(rest...);
};
}
template<class T>
class A
{
public:
using value_type = T; // say
template<class...Rest,
std::enable_if_t<detail::all(std::is_convertible<Rest, value_type>::value...)>* = nullptr>
A(int a, int b, Rest&&...rest)
: a_(a), b_(b)
{
this->fill(std::forward_as_tuple(std::forward<Rest>(rest)...),
std::make_index_sequence<sizeof...(Rest)>());
}
private:
template<class Tuple, std::size_t...Is>
void fill(Tuple&& t, std::index_sequence<Is...> seq)
{
data_.reserve(seq.size());
using expand = int[];
void(expand{ 0,
(data_.push_back(std::move(std::get<Is>(t))), 0)...
});
}
private:
int a_, b_;
std::vector<value_type> data_;
};
int main()
{
using namespace std::literals;
auto a = A<double>(1, 2, 4.3, 5.5, 6.6);
auto b = A<std::string>(1, 2, "the", "cat"s, "sat on the mat");
// error: no matching constructor...
// auto err = A<std::string>(1, 2, "the", "cat"s, 0.1);
}
Here you go:
#include <iostream>
#include <vector>
template<class T>
struct V
{
V(int n, std::initializer_list<T> l)
: data(l)
{
(void) n;
}
std::vector<T> data;
};
int main()
{
V<int> v(0,{1,2,3});
}
This is not a perfect example since one needs to construct an object with the weird syntax (n, {optional, arguments, of, same, type}) but it does provide wanted behavior.
The following example is similar to fr3nzy90's, but with the coming C++17 it will allow automatic deduction of T from the constructor arguments:
template <class T>
class MyContainer {
private:
std::vector<T> data;
public:
// Take the first T value explicitly so it can be used to deduce
// T from the constructor arguments (C++17 feature).
template <class... Ts>
MyContainer(int a, int b, T const & tval, Ts const &... tvals) :
data{tval, tvals...} {
…
}
// Special case, empty list, no implicit type deduction, because
// there is no T value to deduce it from.
MyContainer(int a, int b) {
…
}
};
In C++11, is there a DRY way to construct all elements of an array with some same set of parameters for all elements? (e.g. via a single initializer list?)
For example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
// This would construct just the first object with a parameter of 1.
// For the second and third object the default ctor will be called.
C ar[3] {1};
// This would work but isn't DRY (in case I know I want all the elements in the array to be initialized with the same value.
C ar2[3] {1, 1, 1};
// This is DRYer but obviously still has repetition.
const int initVal = 1;
C ar3[3] {initVal, initVal, initVal};
I know my goal is easily achievable by using an std::vector. I'm wondering if it's possible with raw arrays as well.
c++14 - a little work will make this work for c++11
#include <iostream>
#include <array>
#include <utility>
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
namespace detail {
template<class Type, std::size_t...Is, class...Args>
auto generate_n_with(std::index_sequence<Is...>, const Args&...args)
{
return std::array<Type, sizeof...(Is)> {
{(void(Is), Type { args... })...} // Or replace '{ args... }' with '( args... )'; see in comments below.
};
}
}
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
}
int main()
{
auto a = generate_n_with<C, 3>(1);
for (auto&& c : a)
{
std::cout << c.m_x << std::endl;
}
}
results:
1
1
1
I want to guarantee no copies prior to c++17
The you would need to generate into a vector:
template<class Container, class...Args>
auto emplace_n(Container& c, std::size_t n, Args const&...args)
{
c.reserve(n);
while(n--) {
c.emplace_back(args...);
}
};
used like this:
std::vector<C> v2;
emplace_n(v2, 3, 1);
You can construct a sequence of elements using an std::index_sequence<...> and expand that into the initializers of an array. I don't know of any approach avoiding an auxiliary function, though. Here is an example:
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
#include <array>
#include <iterator>
#include <algorithm>
#include <iostream>
struct S {
int value;
S(int value): value(value) {}
};
std::ostream& operator<< (std::ostream& out, S const& s) {
return out << s.value;
}
template <typename T, std::size_t... I>
std::array<T, sizeof...(I)> fill_aux(T value, std::index_sequence<I...>)
{
return std::array<T, sizeof...(I)>{ (void(I), value)... };
}
template <std::size_t N, typename T>
std::array<T, N> fill(T value) {
return fill_aux(value, std::make_index_sequence<N>());
}
int main()
{
std::array<S, 10> array = fill<10>(S(17));
std::copy(array.begin(), array.end(), std::ostream_iterator<S>(std::cout, " "));
}
By creating derived class, you can effectively create a new default value. It's a bit hackish, but may be less hackish than other solutions. Here's an example:
class C {
public:
C() : C(0) {}
C(int x) : m_x{x} {}
int m_x;
};
template <int init>
struct CInit : C { CInit() : C(init) {} };
CInit<1> ar2[3];
const int initVal = 1;
CInit<initVal> ar3[3];
Another approach is to wrap your raw array inside a struct with a variadic constructor:
template <size_t n>
struct Array {
C array[n];
template <size_t... seq>
Array(int init,std::index_sequence<seq...>)
: array{(void(seq),init)...}
{
}
Array(int init)
: Array(init,std::make_index_sequence<n>())
{
}
};
const int initVal = 1;
Array<3> ar3_1(initVal);
const C (&ar3)[3] = ar3_1.array;
Building on Richard's answer, it's also possible to define
template<class Type, std::size_t N, class...Args>
auto generate_n_with(const std::array<Type, N>&, const Args&...args)
{
return detail::generate_n_with<Type>(std::make_index_sequence<N>(), args...);
};
Allowing you to enter the array as a parameter to make the code more dry in case you already know the type of the array, e.g.
class D {
public:
D();
std::array<int, 3> m_ar;
};
Allowing
D::D() : m_ar{generate_n_with{m_ar, 5}} {}
Instead of the less DRY
D::D() : m_ar{generate_n_with<int, 3>{5}} {}
P.S. maybe there's an even DRYer way without repeating m_ar twice?
I have a templated class containing a std::array<std::vector<T>, N> data_ member.
Currently, I can construct this with
template<typename T, size_t N>
class A
{
public:
A(some parameters)
{
for (size_t n=0; n<N; n++) {
data_[n].resize(calculated size from parameters);
}
}
private:
std::array<std::vector<T>,N> data_;
};
Is there some formulation in which I can instead place the initialization of the array in the constructor?
For example, if I used only a vector and then an indexing function, I could have
...
A(some parameters):
data_(std::vector<T>(N*previous size)) {}
...
#include <array>
#include <vector>
#include <utility>
template <typename T, std::size_t N>
class A
{
public:
A(int a, int b) : A(a, b, std::make_index_sequence<N>{})
{ }
private:
template <std::size_t... Is>
A(int a, int b, std::index_sequence<Is...>)
: data_{ { std::vector<T>(((void)Is, a + b))... } }
{ }
std::array<std::vector<T>, N> data_;
};
DEMO
Since the primary container that you initialise is std::array, and since it only provides aggregate initialisation and you want to parametrise on the size of the array, I'm afraid that you can't do that the way you want. you need to generate an initialisation sequence. This is as far as I know only possible with variadic templates. I tried to put something together, but using the approach from the other answer is clearly the best choice here.
On the other hand there's always the possibility to create a static member function that returns the array as you need it:
Constructor (bool data) : member(initialiser(data)) {}
static array<vector<T>, N> initialiser(bool data) {
array<vector<T>, N> container;
// your code from above
return container;
}
Though it's debatable and dependent on the context whether this is useful or not.