How to initialize an array when using a template - c++

I created an array template for my personal use.
template <typename T, int size>
struct Vector {
T data[size];
};
I tried to intialize the data like so:
Vector<unsigned char, 10> test;
test.data[] = {0,1,2,3,4,5,6,7,8,9};
My compiler ended up complaining something about "expected expression." Does anyone know what I'm doing? I want to be able to use this style of initialization where you give it the entire array definition at once instead of using a for loop to init the elements individually.

Since your class is an aggregate, you can initialize it with the usual brace syntax:
Vector<int, 3> x = { { 1, 2, 3 } };
The exact same thing applies to std::array<int, 3>.

In the new standard, C++11, you can use std::initalizer_list to get the desired result, see the below example.
#include <iostream>
#include <algorithm>
template <typename T, int size>
struct Vector {
T data[size];
Vector<T, size> (std::initializer_list<T> _data) {
std::copy (_data.begin (), _data.end (), data);
}
// ...
Vector<T, size>& operator= (std::initializer_list<T> const& _data) {
std::copy (_data.begin (), _data.end (), data);
return *this;
}
};
int
main (int argc, char *argv[])
{
Vector<int, 10> v ({1,2,3,4,5,6}); // std::initializer_list
v = {9,8,7,6,5,4,3,2,1,0}; // operator=
}
If you are working with a standard prior to C++11 it's a bit more of a hassle really, and your best bet is to implement functions similar to those available when using std::vector.
#include <iostream>
#include <algorithm>
template <typename T, int size>
struct Vector {
T _data[size];
Vector (T* begin, T* end) {
std::copy (begin, end, _data);
}
// ...
void assign (T* begin, T* end) {
std::copy (begin, end, _data);
}
};
int
main (int argc, char *argv[])
{
int A1[4] = {1,2,3,4};
int A2[5] = {99,88,77,66,55};
Vector<int, 10> v1 (A1, A1+4);
// ...
v1.assign (A2, A2+5);
}

You have to supply the type and size of the array when defining the variable:
Vector<int, 10> test;
You can not however assign to the member array like a normal array. You have to assign each element separately:
for (int i = 0; i < 10; i++)
test.data[i] = i; // If instantiated with type "int"

You can only initialize an array at the point you are defining it:
Vector<unsigned char, 10> test;
There's your array, you are done defining it, your chance to initialize it has passed.
Edit: Seeing Mat's answer, memo to me: I have to read up on C++11, and soon... :-/
Edit 2: I just gave the information on what was wrong. Kerrek SB has the information on how to do it right. ;-)

Related

Create const std::vector as the concatenation of two const std::vector

I would like to create a const std::vector to contain all the elements of two other const std::vector of the same type. Since the vector is const I can not concatenate it step by step with the two const std::vector using the method mentioned in Concatenating two std::vectors.
#include <iostream>
#include <vector>
int main()
{
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
const std::vector<int> all_ints;
for (int i: all_ints)
std::cout << i << ' ';
return 0;
}
For the example above I would like to define all_ints in a way that the output is 0 1 2 3.
How could that be done?
Make a function that takes the other two vectors, creates a third one, inserts values from the first two, returns the result by value. Then assign this to your const vector:
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
const std::vector<int> all_ints = concat(int_a, int_b);
I actually don't know what's the essence of creation of const vectors like this. But a simple hack is to create a temporary non-const vector and fill it with the first two vectors, then create the final const vector. eg:
const std::vector<int> int_a{0,1};
const std::vector<int> int_b{2,3};
std::vector<int> middle(int_a);
middle.insert(middle.begin(),int_b.begin(),int_b.end());
const std::vector<int> all_ints(middle);
As suggested in comments, the last line could be written as:
const std::vector<int> all_ints = std::move(middle);
As already mentioned in #Ayxan Haqverdili's answer, you can create a concatenation function that will be used to initialize your vector.
I propose the following implementation for such a function:
template <template <typename, typename> typename C, typename ... Args>
C<Args...> concat(const C<Args...> & lhs, const C<Args...> & rhs)
{
C<Args...> res(lhs.cbegin(), lhs.cend());
res.insert(res.cend(), rhs.cbegin(), rhs.cend());
return res;
}
Note: This implementation is generalized to all standard library sequence containers except std::array.
Which can then be used like this:
const std::vector<int> a {1, 2, 3};
const std::vector<int> b {4, 5};
const std::vector<int> ab = concat(a, b);
Live example here
An alternative and simpler version could be:
template <typename C>
C concat(const C & lhs, const C & rhs)
{
C res(lhs.size() + rhs.size());
typename C::iterator it = std::copy(lhs.cbegin(), lhs.cend(), res.begin());
std::copy(rhs.cbegin(), rhs.cend(), it);
return res;
}
Live example here
Here's yet another implementation that can also easily be modified to even modify the contents of all_ints at a future time. It does not require recent c++ versions.
#include <vector>
#include <algorithm>
int main()
{
const std::vector<int> int_a{ 0,1 };
const std::vector<int> int_b{ 2,3 };
const std::vector<int> all_ints(int_a.size()+ int_b.size());
std::vector<int>& all_intsr = const_cast<std::vector<int>&>(all_ints);
std::copy(int_b.begin(), int_b.end(), std::copy(int_a.begin(), int_a.end(), all_intsr.begin()));
}
This takes advantage of a being able to legally cast a const vector& to a vector with the following restriction to prevent UB. One may not modify the vector object. This does not include the contents owned by the vector which is not const. Neither begin() or end() modify the vector object. Also modifying element of it later are legal such as this.
all_intsr[3]=42;
If you are just looking to iterate over both vectors, you can do it using a custom view concatenator. The following is much more efficient than creating (and destroying) another container just to iterate over both ranges.
#include <iostream>
#include <array>
#include <ranges>
#include <vector>
#include <utility>
template<typename T1, typename T2>
auto concat_view(T1& lhs, T2& rhs)
{
static_assert(std::is_same_v<std::decay_t<T1>, std::decay_t<T2>>);
using T1_ = std::remove_reference_t<T1>;
using T2_ = std::remove_reference_t<T2>;
if constexpr (std::is_const_v<T1_> || std::is_const_v<T2_>)
{
using Iter = typename std::decay_t<T1>::const_iterator;
return std::array<std::ranges::subrange<Iter>, 2>{std::as_const(lhs), std::as_const(rhs)} | std::views::join;
}
else
{
using Iter = typename std::decay_t<T1>::iterator;
return std::array<std::ranges::subrange<Iter>, 2>{lhs, rhs} | std::views::join;
}
}
int main()
{
std::vector<int> v1{1,2,3}, v2{4,5,6};
for (int& val : concat_view(v1, v2))
++val;
for (const auto& val : concat_view(std::as_const(v1), v2))
std::cout << val << '\n';
return 0;
}

std::array Type Initialization

With a std::array you can initialize it like this:
std::array<int,5> myArray = {1,2,3,4,5};
If I where trying to create my own array class how would I do something similar?
std::array rely on aggregate initialization (the C way of initializing struct), basically this is valid c++:
struct A {
int values[2];
size_t size;
};
A a = {{42, 44}, 2U}; // No constructor involved
// | |--- a.size
// |--- a.values
Now if you remove the size attribute, you get:
struct A {
int values[2];
};
A a = {{42, 44}}; // Valid!
But c++ gives you something called brace-elision we allow you to omit the inner brackets, so:
A a = {42, 44}; // Still valid!
Now, just makes A a template:
template <typename T, size_t N>
struct A {
T data[N];
};
A<int, 2> a = {{42, 44}};
Note that this does not makes use of initializer_list which are used for std::vector, std::list, and so on.
Also note that:
A<int, 2> a1{42, 44}; // Not valid prior to c++14
A<int, 2> a2{{42, 44}}; // Ok even for c++11
And note that clang will throw warning whatever the version you use by default.
A minimalist version of std::array could look like this:
template <typename T, size_t N>
struct my_array {
T _data[N];
T& operator[] (size_t i) { return _data[i]; }
const T& operator[] (size_t i) const { return _data[i]; }
constexpr size_t size () const { return N; }
T* begin () { return _data; }
const T* begin () const{ return _data; }
T* end () { return _data + size(); }
T* end () const { return _data + size(); }
};
Please note that the standard std::array has a lot more stuff than this (a lots of typedefs, other methods, a lots of overloads, ...), but this is a small base to get you started.
Also note that _data has to be public so that aggregate initialization can work (it does not work if you have private / protected members!).
Here is a simple implementation that supports initializing with a list of elements:
//template class 'array' takes 'T' (the type of the array) and 'size'
template<typename T, std::size_t size>
class array
{
public:
//This is the concept of initializer lists (wrapper around variadic arguments, that
//allows for easy access)
array(std::initializer_list<T> list)
{
//Loop through each value in 'list', and assign it to internal array
for (std::size_t i = 0; i < list.size(); ++i)
arr[i] = *(list.begin() + i); //list.begin() returns an iterator to the firsts
//element, which can be moved using +
//(deferenced to get the value)
}
//Overloads operator[] for easy access (no bounds checking!)
T operator[](std::size_t index)
{
return arr[index];
}
private:
T arr[size]; //internal array
};
You can then use it like so:
array<int, 2> a = { 2, 5 };
int b = a[0]; //'b' == '2'

How to properly static cast a vector in C++?

I have a code in which at the end of a function I need to cast from int to double all the elements of an array in order to being able to do a final push_back before exiting the function. The code I have right now is:
template <class T, size_t dims> class A {
typedef typename std::array<int, dims> ArrayInt;
typedef typename std::array<double, dims> ArrayDouble;
typedef typename std::vector <ArrayDouble> VectorDouble;
/* ...*/
foo() {
/* ...*/
ArrayInt myArrayInt;
ArrayDouble myArrayDouble;
VectorDouble myVectorDouble;
/* Initialize myArrayInt
Do some other stuff */
for (int i = 0; i < dims; ++i)
myArrayDouble[i] = static_cast<double>(myArrayInt[i]);
myVectorDouble.push_back(myArrayDouble);
}
}
It works properly, but I do not feel comfortable about the lines:
for (int i = 0; i < dims; ++i)
myArrayDouble[i] = static_cast<double>(myArrayInt[i]);
Is there any better way of doing this?
Thank you.
You can use a function from algorithm.
With copy_n :
std::copy_n( myArrayInt.begin(), dims, myArrayDouble.begin() );
or with copy :
std::copy( myArrayInt.begin(), myArrayInt.end(), myArrayDouble.begin() );
This can be written with less code, but it is explicit.
ArrayInt myArrayInt;
ArrayDouble myArrayDouble;
VectorDouble myVectorDouble;
/* Initialize myArrayInt
Do some other stuff */
using std::transform;
using std::copy;
using std::begin;
using std::end;
// with explicit conversion
auto to_double = [](const int i) { return double{i}; };
transform(begin(myArrayInt), end(myArrayInt), begin(myArrayDouble),
to_double);
// with implicit conversion
copy(begin(myArrayInt), end(myArrayInt), begin(myArrayDouble));

C++-equivalent to incompletely initialized arrays in C?

When I transform code from C to C++ I sometimes encounter language constructs that are C, but compatible with C++. Usually I want to transform the code in the least intrusive way. But I have one case where I find that very difficult:
In C you can declare an array and initializing... well... parts of it using "designators", the rest is zeroed out (Edit: I wrote "left to randomness" here, first):
int data[7] = {
[2] = 7,
[4] = 9,
};
This is not valid C++-code, though (luckily). So I will have to use a different strategy.
While I can see a non-intrusive way in C++11:
static const map<int,int> data = { {2,7}, {4,9} };
what should I do when C++11-features are not available yet?
Can I circumvent a runtime initialization?
Is there a way to initialize similar kind of mapping in a "literal" way?
What is least intrusive to the code that uses data?
Well unless the size of the array is totally insane, you can always do this
int data[7] = {
0,
0,
7, // #2
0,
9 // #4
// the rest will be 0-initialized
};
Works in compile time too
If uniform initialization is not available, the std::map<int, int> could be initialized using boost::assign::map_list_of:
#include <boost/assign/list_of.hpp>
static const std::map<int,int> data = boost::assign::map_list_of(2,7)(4,9);
Rather than using map<int, int> you can backport std:array (or a minimal equivalent) from C++11, and use a Boost.Assign-style builder facility:
#include <cstddef>
template<typename T, size_t N> struct array { T data[N]; };
template<typename T, size_t N> struct build_array: public array<T, N> {
build_array &operator()(size_t i, const T &t) {
this->data[i] = t;
return *this;
}
};
array<int, 7> data_array = build_array<int, 7>()(2, 7)(4, 9);
int (&data)[7] = data_array.data;
Why can't you do:
int data[7];
data[2] = 7;
data[4] = 9;
looks very-very similar =)
If you do not want to use boost::assign
You can create it's simple analogue:
template<class T1, class T2>
std::map<T1, T2> cre(std::map<T1, T2> & m)
{
return std::map<T1, T2>();
}
template<class T1, class T2>
std::map<T1, T2> & ass(std::map<T1, T2> & m, T1 const & p1, T2 const & p2)
{
m[p1] = p2;
return m;
}
std::map<int, int> data = ass(ass(cre(data), 2, 3), 7, 6);

How do I turn relational comparison of pointers into an error?

We've been bitten by the following bug many times:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
void print(int* pn) { cout << *pn << " "; }
int main() {
int* n1 = new int(1);
int* n2 = new int(2);
int* n3 = new int(3);
vector<int*> v;
v.push_back(n1);
v.push_back(n2);
v.push_back(n3);
sort(v.begin(), v.end()); // Here be dragons!
for_each(v.begin(), v.end(), print);
cout << endl;
delete n1; delete n2; delete n3;
}
The problem is that std::sort is comparing integer pointers not integers, which is not what the programmer intended. Worse, the output may appear correct and deterministic (consider the order of addresses returned by new or allocated on the stack). The root problem is that sort eventually calls operator< for T, which is rarely a good idea when T is a pointer type.
Is there any way to prevent this or at least get a compiler warning? For example, is there a way to create a custom version of std::sort that requires a comparison function when T is a pointer?
IMO, the programmers should know that std::sort assumes the container stores values. If you need a different behavior for the comparison, then you provide a comparison function. E.g. (untested):
template<typename T>
inline bool deref_compare(T* t1, T* t2) { return *t1 < *t2; }
//...
std::sort(v.begin(), v.end(), deref_compare<int>);
Edit
FWIW, Jacob's answer comes closest to directly accomplishing what you want. There might be some ways to generalize it further.
For pointers in general you could do this:
#include <ctime>
#include <vector>
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <type_traits>
namespace util {
struct sort_pointers {
bool operator() ( int *a, int *b ) {
return *a < *b;
}
};
template <typename T, bool is_pointer = !std::tr1::is_pointer<T>::value>
struct sort_helper {
typedef std::less<T> wont_compare_pointers;
};
template <typename T>
struct sort_helper<T,false> {
};
template <typename Iterator>
void sort( Iterator start, Iterator end )
{
std::sort( start,
end,
sort_helper
<
typename Iterator::value_type
>::wont_compare_pointers() );
}
template <typename Iterator, class Func>
void sort( Iterator start, Iterator end, Func f ) {
std::sort( start, end, f );
}
}
int main() {
std::vector<int> v1;
std::vector<int*> v2;
srand(time(0));
for( int i = 0; i < 10; ++i ) {
v1.push_back(rand());
}
util::sort( v1.begin(), v1.end() );
for( int i = 0; i < 10; ++i ) {
v2.push_back(&v1[i]);
}
/* util::sort( v2.begin(), v2.end() ); */ //fails.
util::sort( v2.begin(), v2.end(), util::sort_pointers() );
return 0;
}
std::tr1::is_pointer was just what it was called in Visual Studio 2008, but I think Boost has one too, and newer compiles might provide it as std::is_pointer. I'm sure someone would be able to write a prettier solution, but this appears to work.
But I must say, I agree with cogwheel, there is no reason for this, the programmer should be able to see if this is going to be a problem and act accordingly.
Addition:
You can generalize it a bit more I think, to automatically select a functor that will dereference the pointers and compare the values:
namespace util {
template <typename T>
struct sort_pointers {
bool operator() ( T a, T b ) {
return *a < *b;
}
};
template <typename T, bool is_pointer = !std::tr1::is_pointer<T>::value>
struct sort_helper {
typedef std::less<T> compare;
};
template <typename T>
struct sort_helper<T,false> {
typedef sort_pointers<T> compare;
};
template <typename Iterator>
void sort( Iterator start, Iterator end )
{
std::sort( start,
end,
sort_helper
<
typename Iterator::value_type
>::compare() );
}
}
That way you don't have to think about if you're providing it with pointers to compare or not, it will automatically be sorted out.
I don't have a good answer for pointers in general, but you can restrict comparisons if you're using a smart pointer of any kind - eg boost::shared_ptr.
#include <boost/shared_ptr.hpp>
using namespace std;
template<class T>
bool operator<(boost::shared_ptr<T> a, boost::shared_ptr<T> b)
{
return boost::shared_ptr<T>::dont_compare_pointers;
}
int main () {
boost::shared_ptr<int> A;
boost::shared_ptr<int> B;
bool i = A < B;
}
Output:
In function 'bool operator<(boost::shared_ptr<T>, boost::shared_ptr<T>) [with T = int]':
t.cpp:15: instantiated from here
Line 8: error: 'dont_compare_pointers' is not a member of 'boost::shared_ptr<int>'
compilation terminated due to -Wfatal-errors.
So you can use smart pointers, or create your own smart pointer wrapper. This is very heavyweight for what you want though, so if you do create a wrapper to detect this situation, I recommend you only use it in debug mode. So create a macro (ugh, I know) and use it to declare pointers.
#ifdef DEBUG
#define pointer(x) pointer_wrapper<X>
#else
#define pointer(x) x*
#endif
This still requires your programmers to use it, of course!