I have come across many occasions where I want to have an item which is selected inside a vector, for this I have written the template class:
// a vector wrapper which allows a specific item to be currently selected
template<typename T>
class VectorSelectable
{
public:
VectorSelectable() {};
VectorSelectable(std::initializer_list<T> items) : m_Items(items) {};
void Add(const T& v) { m_Items.push_back(v); m_CurrentIndex = m_Items.size()-1; } // lvalue & refs
void Add(T&& v) { m_Items.push_back(std::move(v)); m_CurrentIndex = m_Items.size()-1; } // rvalue
void Remove(size_t index) {
assert(index < m_Items.size());
m_Items.erase(m_Items.begin() + index);
if(m_CurrentIndex != -1 && (int)index <= m_CurrentIndex)
m_CurrentIndex--;
}
void RemoveCurrent() { assert(m_CurrentIndex > -1 && m_CurrentIndex < (int)m_Items.size()); Remove(m_CurrentIndex); }
T& CurrentItem() { assert(m_CurrentIndex > -1 && m_CurrentIndex < (int)m_Items.size()); return m_Items[m_CurrentIndex]; }
T& operator [](size_t index) { assert(index < Size()); return m_Items[index]; }
// moves value of n_next onto n, and n_new onto n
void ItemSwap(size_t n, size_t n_Next) {
assert(n < m_Items.size());
assert(n_Next < m_Items.size());
T itemBuf = std::move(m_Items[n]);
m_Items[n] = m_Items[n_Next];
m_Items[n_Next] = std::move(itemBuf);
}
size_t Size() { return m_Items.size(); }
const std::vector<T>& Data() { return m_Items; }
std::vector<T>* DataPtr() { return &m_Items; }
T* ItemPtr(size_t index) { assert(index < m_Items.size()); return &m_Items[index]; }
void SetCurrentIndex(int index) { assert(index >= -1 && index < (int)m_Items.size()); m_CurrentIndex = index; }
int& CurrentIndex() { return m_CurrentIndex; }
bool HasItemSelected() { return m_CurrentIndex != -1; }
private:
std::vector<T> m_Items;
int m_CurrentIndex = -1;
};
I am also coming across many scenarios where I want a vector of unique_ptrs (generally for polymorphic classes), this looks like this:
template<typename T>
class Vector_UniquePtrs
{
public:
// Adds an Item (and returns a raw ptr to it)
// usage: v.Add() ... (equivelent to v.Add<base_class>())
template<typename... Args>
T* Add(Args... args) {
return Add<T>(args...);
}
// Adds a Polymorphic Item (and returns a raw ptr to it)
// usage: v.Add<sub_class>()
template<typename T2, typename... Args>
T* Add(Args... args) {
m_Items.push_back(std::unique_ptr<T>(new T2(args...)));
return m_Items.back().get();
}
// Remove Item
void Remove(size_t index) {
assert(index < m_Items.size());
m_Items.erase(m_Items.begin() + index);
}
T* operator [](size_t index) { assert(index < Size()); return m_Items[index].get(); }
size_t Size() { return m_Items.size(); }
private:
std::vector<std::unique_ptr<T>> m_Items;
};
My question is:
How can I handle a combination of these 2 class types (e.g. VectorSelectable<unique_ptr>) as one returns ptrs, the other returns references, is the only option to write an entirely new class?
You mainly need to put the std::vector<std::unique_ptr<T>> in VectorSelectable and hide all the pointer stuff from the interface. With a few small changes to your class, it could look like this:
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <utility>
#include <vector>
template <typename T>
class VectorPtrSelectable {
public:
VectorPtrSelectable() = default;
VectorPtrSelectable(std::initializer_list<T> items) :
m_CurrentIndex(items.size() - 1)
{
m_Items.reserve(items.size());
// fill `m_Items` from the initializer list ...
std::transform(items.begin(), items.end(), std::back_inserter(m_Items),
[](const T& item) {
// ... by creating a unique_ptr from each element (transformation)
return std::make_unique<T>(item);
});
};
template <class U, class... Args>
T& Add(Args&&... args) {
// make `Add` forward to `make_unique`
m_Items.emplace_back(std::make_unique<U>(std::forward<Args>(args)...));
m_CurrentIndex = m_Items.size() - 1;
// and return a reference instead
return *m_Items.back();
}
template <class... Args>
T& Add(Args&&... args) {
// forward to Add<U>
return Add<T>(std::forward<Args>(args)...);
}
void Remove(size_t index) {
m_Items.erase(std::next(m_Items.begin(), index));
if (m_CurrentIndex != static_cast<size_t>(-1) && index <= m_CurrentIndex)
m_CurrentIndex--;
}
T& operator[](size_t index) { return *m_Items[index]; }
const T& operator[](size_t index) const { return *m_Items[index]; }
T& CurrentItem() { return *m_Items[m_CurrentIndex]; }
const T& CurrentItem() const { return *m_Items[m_CurrentIndex]; }
void SetCurrentIndex(size_t index) { m_CurrentIndex = index; }
void RemoveCurrent() { Remove(m_CurrentIndex); }
bool HasItemSelected() { return m_CurrentIndex != static_cast<size_t>(-1); }
void ItemSwap(size_t n, size_t n_Next) {
// simplified swapping:
std::swap(m_Items[n], m_Items[n_Next]);
}
// make functions that does not change your instance const qualified:
size_t CurrentIndex() const { return m_CurrentIndex; }
size_t Size() const { return m_Items.size(); }
private:
std::vector<std::unique_ptr<T>> m_Items;
size_t m_CurrentIndex = static_cast<size_t>(-1); // size_t for the index
};
Example usage:
#include <iostream>
#include <string>
int main() {
VectorPtrSelectable<std::string> vs{"World", "Hello"};
std::cout << vs.CurrentItem() << '\n';
vs.ItemSwap(0, 1);
std::cout << vs.CurrentItem() << '\n';
vs.RemoveCurrent();
std::cout << vs.CurrentItem() << '\n';
std::cout << vs.Add("Add and get a reference") << '\n';
}
Output:
Hello
World
Hello
Add and get a reference
I made m_CurrentIndex a size_t because that's idiomatic but if you'd like to keep it as an int, that's fine too.
std::next(m_Items.begin(), index) will do the same as m_Items.begin() + index, but in cases where the iterator returned by m_Items.begin() is a plain pointer, using std::next avoids potential warnings about using pointer arithmetic.
Returning a reference instead of a pointer to the added element makes no difference other than making the interface more idiomatic. It's simply what a user of the class is likely to expect. Returning a pointer also opens up questions like "can it return nullptr?" etc.
The added const qualified functions makes those functions usable in const contexts too.
template<class T>
void foo(const VectorPtrSelectable<T>& vps) { // note: const&
if(vps.Size() > 0) {
std::cout << "the first element is " << vps[0] << '\n';
std::cout << "the current element is " << vps.CurrentItem() << '\n';
}
}
None of the three member functions used above could be used without the const qualified overloads.
Related
I have a 2d matrix using vector library. And I wanted to iterate over the Matrix more conveniently, so I created an MatrixIterator class.
Matrix.cpp
#include <vector>
template <class T>
class MatrixIterator;
template <class T>
class Matrix
{
friend class MatrixIterator<T>;
private:
public:
std::vector<std::vector<T>> m;
unsigned rows_;
unsigned cols_;
Matrix<T>(unsigned rows, unsigned cols);
MatrixIterator<T> iterator() const
{
return {*this};
}
MatrixIterator<T> begin() const
{
return {*this};
}
MatrixIterator<T> end() const
{
return {*this, rows_, 0};
}
}
template <class T>
class MatrixIterator
{
private:
Matrix<T> matrix_;
unsigned row_;
unsigned col_;
public:
MatrixIterator<T>(Matrix<T> m) : matrix_(m), row_(0), col_(0) {};
MatrixIterator<T>(Matrix<T> m, unsigned row, unsigned col) : matrix_(m), row_(row), col_(col) {};
MatrixIterator<T> begin() const
{
return {matrix_};
}
MatrixIterator<T> end() const
{
return {matrix_, matrix_.rows_, 0};
}
void inc()
{
if(++col_ >= matrix_.cols_)
{
row_++;
col_ = 0;
}
}
MatrixIterator<T>& operator++()
{
inc();
return *this;
}
MatrixIterator<T> operator++(int)
{
inc();
return *this;
}
bool operator!=(const MatrixIterator<T> &rhs) const
{
return (row_ != rhs.row_) || (col_ != rhs.col_);
}
T& operator*()
{
return matrix_.m[row_][col_];
}
};
template <class T>
Matrix<T>::Matrix(unsigned rows, unsigned cols)
: rows_(rows), cols_(cols)
{
m.resize(cols);
for (unsigned i = 0; i < cols; i++)
{
m[i].resize(rows);
fill(m[i].begin(), m[i].end(), T());
}
}
In the following code, when I try to manipulate value using iterator, it does not change the value.
I tried returning the values as pointers from operator* but it did not work either. I saw no errors. What is wrong and how can I solve this?
main.cpp
#include "Matrix.cpp"
#include<iostream>
int main()
{
Matrix<int> m = Matrix<int>{3,3};
for(auto x: m.iterator())
x = 10;
for(auto x: m.iterator())
std::cout << x << " ";
// outputs 0 0 0 ~
}
Compiled with g++ main.cpp -std=c++20 -g -o main && main
You need to store a reference in the iterator class, other than hold a copy of it (iterator is just a view of the data).
template <class T>
class MatrixIterator {
private:
Matrix<T>& matrix_;
unsigned row_;
unsigned col_;
public:
MatrixIterator<T>(Matrix<T>& m) : MatrixIterator<T>(m, 0, 0) {}
MatrixIterator<T>(Matrix<T>& m, unsigned row, unsigned col)
: matrix_(m), row_(row), col_(col) {}
};
And you also need to non-const begin and end for your matrix, nonconst version iterator can be used to change the underlying value. The function iterator() can be removed here, since it's not common to have this in c++ code.
MatrixIterator<T> begin() const { return {*this}; }
MatrixIterator<T> begin() { return {*this}; }
MatrixIterator<T> end() const { return {*this, rows_, 0}; }
MatrixIterator<T> end() { return {*this, rows_, 0}; }
To change to the value with an iterator, you need a reference other than changing the copied value in your main function. It's not necessary to explicitly call the iterator here, the compiler will do it for you.
int main() {
Matrix<int> m = Matrix<int>{3, 3};
for (auto& x : m) x = 10;
for (auto x : m) std::cout << x << " ";
return 0;
}
Online demo.
You are iterating over values, not references when attempting to change the matrix values. Instead, try
for (auto& x : m.iterator())
In C, one can assign a data pointer to a void pointer and then cast it back to the original type, that data pointer will be recovered. The language standard guarantees that such transformation does not lose information. This often means (not necessarily, but true for most platforms) that the size of void pointer is the same with that of data pointers. Thus one can count on these facts to use void pointers as general pointers to heterogeneous types while void pointers themselves are of uniform size and representation. For example, one has an array of void pointers, with its elements pointing to dynamically allocated objects of different types. Constructing such an array makes certain things convenient.
My question is: How does one implement something similar, a general pointer type in C++, which comply with the following: (assume g_pointer is the class name )
Constructed from any pointer types, one can write code like
g_pointer g_ptr = g_pointer(new T())
Recover the original pointer
T* ptr = g_ptr.recover(), or
auto* ptr = g_tr.recover()
Update: According to some comments, the above couldn't be done in C++, then something like
recover<Type>(g_ptr)
should suffice, throwing an exception Type is not compatible.
g_pointer can be contained in std::vector or a plain array, that is basically means
sizeof(g_pointer) // a predetermined constant number,
(Update: This is always true, provided such a class can be correctly implemented, thanks for pointing out.)
I have just found boost::any, a peek into its introduction seems suggeesting that it may be what I want, although it might not be the case. So anyone who is familiar with boost::any is welcomed to comment.
Update: (response to some comments)
A g_pointer type object should be aware of the underlying type of the object to which it points. thus the recover method should always return a pointer of that type.
A general pointer type, meaning a reference to ANY object, IMHO, is a reasonable thing to ask to any language supporting object-oriented paradigm.
Update: Thanks #Caleth, std::any is great.
It is impossible in C++. Because the type of the expression g_ptr.recover() is determined at compile time, it cannot store information of the underlying type, which is determined at runtime.
If you can tolerate expressions like g_ptr.recover<T>(), you can implement g_pointer by wrapping a void* and a const std::type_info& that stores the information of the actual type the pointer points to, e.g.
class g_pointer {
public:
template <class T>
constexpr g_pointer(T *data) noexcept : _data(data), _object_type(typeid(T)) {}
template <class T>
T* recover() const {
if (typeid(T) == _object_type) return static_cast<T*>(_data);
else throw std::bad_cast{};
}
private:
void *_data;
const std::type_info &_object_type;
};
Note this g_pointer behaves like a raw pointer rather than a smart pointer, which means it does not own the object it points to.
There is still a defect in the implementation above: const T* cannot be implicitly converted to void*, thus the general pointer cannot hold const T*. To handle const-qualifiers, you can change the type of _data to const void* and use const_cast when recovering. In addition, recover shall reject to return a pointer to non-const object from a g_pointer holding a pointer to const object. However, typeid operator ignores top const-qualifiers, so we need an additional data member to record whether the pointer points to an originally const object.
class g_pointer {
public:
template <class T>
constexpr g_pointer(T *data) noexcept : _data(data),
_object_type(typeid(T)),
_is_const(std::is_const_v<T>)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ change here
{
}
template <class T>
T* recover() const {
if (
typeid(T) != _object_type ||
(_is_const && !std::is_const_v<T>) // try to obtain T* while const T* is held
) {
throw std::bad_cast{};
}
else return static_cast<T*>(const_cast<void*>(_data));
// ^^^^^^^^^^^^^^^^^ change here
}
private:
const void *_data;
// ^^^^^ change here
const std::type_info &_object_type;
bool _is_const; // <-- record whether the pointer points to const T
};
There's nothing stopping you from using C constructs, like a void*, in C++. It's generally frowned upon, however, because it can open the door for various bugs should the code be used in ways unintended, or the consequences of said actions not being fully documented.
That being said, you're essentially asking to wrap a void* in a class that can then be used in a std::vector and then accessed later.
Here's some code from a framework I wrote some time ago to sort of achieve a similar effect:
generic_ptr.hpp
#include <exception>
#include <typeinfo>
#include <map>
namespace so {
std::map<std::size_t, std::size_t> type_sizes;
template < typename T >
std::size_t type_id()
{
static char tid;
std::size_t sz = reinterpret_cast<std::size_t>(&tid);
so::type_sizes[sz] = sizeof(T);
return sz;
}
template < typename T >
inline std::size_t type_id(const T& t)
{
return so::type_id<T>();
}
template < typename T >
inline std::size_t type_id(const T *const t)
{
return so::type_id<T>();
}
template < typename T, typename C >
inline bool type_of()
{
return so::type_id<T>() == so::type_id<C>();
}
template < typename T, typename C >
inline bool type_of(const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const C *const c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T& t, const C& c)
{
return so::type_of<T, C>();
}
template < typename T, typename C >
inline bool type_of(const T *const t, const C *const c)
{
return so::type_of<T, C>();
}
class generic_ptr
{
public:
generic_ptr() : m_ptr(0), m_id(0) { }
template < typename T >
generic_ptr(T *const obj) :
m_ptr(obj), m_id(so::type_id<T>())
{
}
generic_ptr(const generic_ptr &o) :
m_ptr(o.m_ptr), m_id(o.m_id)
{
}
~generic_ptr()
{
this->invalidate();
}
static generic_ptr null()
{
return generic_ptr();
}
void invalidate()
{
this->m_ptr = 0;
this->m_id = 0;
}
template < typename T >
bool is_type() const
{
return this->m_id == so::type_id<T>();
}
template < typename T >
void gc()
{
delete ((T*)this->m_ptr);
this->invalidate();
}
bool valid() const
{
return (this->m_ptr != 0);
}
operator bool() const
{
return (this->m_ptr != 0);
}
bool operator!() const
{
return (!operator bool());
}
generic_ptr& operator=(const generic_ptr &o)
{
this->m_ptr = o.m_ptr;
this->m_id = o.m_id;
return *this;
}
template < typename T >
const generic_ptr& operator=(T *const obj)
{
this->m_ptr = obj;
this->m_id = so::type_id<T>();
return *this;
}
template < typename T >
operator T *const() const
{
if (this->m_id != so::type_id<T>()) {
throw std::bad_cast();
}
return static_cast<T *const>(
const_cast<void *const>(this->m_ptr)
);
}
template < typename T >
operator const T *const() const
{
if ((this->m_id != so::type_id<T>()) && (this->m_id != so::type_id<const T>())) {
throw std::bad_cast();
}
return static_cast<const T *const>(this->m_ptr);
}
operator void *const() const
{
return const_cast<void*>(this->m_ptr);
}
operator const void *const() const
{
return this->m_ptr;
}
bool operator==(const generic_ptr& o) const
{
return (this->m_ptr == o.m_ptr && this->m_id == o.m_id);
}
bool operator!=(const generic_ptr& o) const
{
return !(*this == o);
}
std::size_t hash() const
{
return this->m_id;
}
private:
const void* m_ptr;
std::size_t m_id;
};
}
Then to use it:
main.cpp
#include <iostream>
#include <vector>
#include "generic_ptr.hpp"
class MyClass {
public:
MyClass() : m_val1(10), m_val2(20), m_val3(10), m_val4(2) {}
MyClass(int a, int b, int c, int d) : m_val1(a), m_val2(b), m_val3(c), m_val4(d) {}
friend std::ostream& operator<<(std::ostream& os, const MyClass& mc)
{
os << mc.m_val1 << " + " <<
mc.m_val2 << " + " <<
mc.m_val3 << " + " <<
mc.m_val4 << " = " <<
(mc.m_val1 + mc.m_val2 + mc.m_val3 + mc.m_val4);
return os;
}
private:
int m_val1;
int m_val2;
int m_val3;
int m_val4;
};
template < typename T >
void print(so::generic_ptr& g_ptr)
{
std::cout << "sizeof = " << so::type_sizes[g_ptr.hash()]
<< ", val = " << *((T*)g_ptr) << std::endl;
}
template < typename T >
void cleanup(so::generic_ptr& g_ptr)
{
delete ((T*)g_ptr);
}
int main(int argc, char* argv[])
{
std::vector<so::generic_ptr> items;
items.push_back(new int(10));
items.push_back(new double(3.14159));
items.push_back(new MyClass());
items.push_back(new char(65));
items.push_back(new MyClass(42,-42,65536,9999));
items.push_back(new int(999));
for (auto i : items) {
if (i.is_type<int>()) { print<int>(i); }
else if (i.is_type<char>()) { print<char>(i); }
else if (i.is_type<double>()) { print<double>(i); }
else if (i.is_type<MyClass>()) { print<MyClass>(i); }
}
int* i = (int*)items[0];
std::cout << "i = " << *i << std::endl;
*i = 500;
std::cout << "i = " << *i << std::endl;
try {
double* d = (double*)items[0];
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
for (auto i : items) {
if (i.is_type<int>()) {
print<int>(i);
cleanup<int>(i);
} else if (i.is_type<char>()) {
print<char>(i);
cleanup<char>(i);
} else if (i.is_type<double>()) {
print<double>(i);
cleanup<double>(i);
} else if (i.is_type<MyClass>()) {
print<MyClass>(i);
cleanup<MyClass>(i);
}
}
return 0;
}
Of course, you still have to know the type and keep track of memory, but you could modify the code to handle that; using the operator overloads, you don't need a recover function in this manner, you can just do a cast, like in the print code: *((T*)g_ptr), and can access it via raw pointers, like before the last for..each statement:
int* i = (int*)items[0];
*i = 500;
print<int>(items[0]);
This class also has invalid type casting built in, in the event you try and cast between invalid types:
try {
double* d = (double*)items[0];
// items[0] is an int, so this will throw a std::bad_cast
std::cout << "d = " << *d << std::endl;
} catch (std::bad_cast& ex) {
std::cout << ex.what() << std::endl;
}
To be honest though, convenience could trump security in this instance, so if you need an array of types that are not consistent or that can not be defined using a base class, you might need to rethink what you're trying to achieve in a C++ manner.
I hope that can help you get some clarity.
So i'm trying to create a vec class that I can do vec math on at a later point.
Was working great until I began Implementing actual procedures on it.
Class vecn:
#include <vector>
template <typename T>
class vecn
{
public:
vecn() { }
template <typename... Args>
vecn(Args&&... args)
{
addtovector(args...);
}
friend std::ostream& operator<<(std::ostream& os, const vecn<T>& obj)
{
os << "{";
for (auto it = obj.contents.begin(); it < obj.contents.end(); it++) {
os << *it;
if (it != obj.contents.end() -1)
{
os << ",";
}
}
os << "}";
return os;
}
template<typename T>
vecn<T>& operator=(const vecn<T>& v) {
contents = v.contents;
return *this;
}
unsigned int size() const
{
return contents.size();
}
vecn<T> operator+(const vecn<T>& v1) {
vecn<T> v2();
for (unsigned int i = 0; i < size();i++)
{
v2[i] = v1[i] + this->contents[i];
}
return v2;
}
T& operator[](size_t Index)
{
if (Index > contents.size() -1)
{
contents.resize(Index + 1);
}
return contents.at(Index);
}
const T& operator[](size_t Index) const
{
return contents.at(Index);
}
private:
template <typename... Args>
void addtovector(T& first, Args&&... args)
{
addtovector(first);
addtovector(args...);
}
void addtovector(T& item)
{
contents.push_back(item);
}
std::vector<T> contents;
};
Now i'm having a problem with acceing the underling vector using the subscript operator, no matter how I design it, it never quite works. Usually resulting in a
Error C2109 subscript requires array or pointer type
From Googling the error, I'm supposed to return a pointer to the array with the subscript. which I do.
Is there something i'm missing?
main:
vecn<int> v(1,2);
vecn<int> b(3, 4, 5);
std::cout << v + b;
Expected output:
{4,6,5}
GCC told me exactly what's wrong:
error: declaration of template parameter ‘T’ shadows template parameter
(for your assignment operator)
Then:
warning: pointer to a function used in arithmetic [-Wpointer-arith]
v2[i] = v1[i] + this->contents[i];
(you've declared v2 as a function returning vecn<T>, remove the parentheses)
Lastly, fix your operator+, because it will try to access elements of an empty v2 vector and access out of range of the smaller of v1 and this->contents if their sizes are not equal.
It seems to me that you have wrote a lot of unnecessary code for such a simple thing. You don't need addtovector, just expand the parameter pack like:
contents{std::forward<Args>(args)...}
in the member initializer list. You don't need to define operator= at all, leave it up to the compiler. And try to implement operator+ in terms of operator+=.
Despite all other things you have one rather creepy thing in your code
if (Index > contents.size() -1)
You should never ever replace the correct choice of the boolean operator by arithmetic operations! std::vector<>.size() returns size_t (unsigned long on most systems), only your selfmade size() returns int, which it shouldn´t since there is no meaning in negative sizes.
Now 0 - 1 doesn´t yield a negative with 0 being size_t but something huuuge: 18446744073709551615 (0xffffffffffffffff).
So with size()=0
if (Index > contents.size() -1)
Will never ever be true and your vec will not grow, as you wanted, on access to vec[0]. Simply use
if (Index >= contents.size())
Which is exactly what you mean.
Likely your problem was that you used vecn<T> v2(); instead of vecn<T> v2;, but there are another bad things in your solution. Here is some refactor:
template <typename T>
class vecn {
public:
vecn() {}
template <typename... Args>
vecn(Args&&... args)
{
addToVector(args...);
}
friend std::ostream& operator<<(std::ostream& os, const vecn<T>& obj)
{
os << "{";
for (auto it = obj.contents.begin(); it < obj.contents.end(); it++) {
os << *it;
if (it != obj.contents.end() - 1)
os << ",";
}
os << "}";
return os;
}
// you don't need this
// template<typename T>
// also whole method is unnecessary
// vecn<T>& operator=(const vecn<T>& v)
// {
// contents = v.contents;
// return *this;
// }
// use size_t
/*unsigned int*/ size_t size() const
{
return contents.size();
}
vecn<T> operator+(const vecn<T>& other) const
{
vecn<T> result;
size_t resultSize = std::max(other.size(), size());
result.contents.reserve(resultSize);
for (size_t i = 0; i < resultSize; ++i) {
T value = {};
if (i < other.size())
value += other.contents[i];
if (i < size())
value += contents[i];
result.contents.push_back(value);
}
return result;
}
T& operator[](size_t index)
{
return contents.at(index);
}
const T& operator[](size_t index) const
{
return contents.at(index);
}
private:
template <typename... Args>
void addToVector(T& first, Args&&... args)
{
addToVector(first);
addToVector(args...);
}
void addToVector(T& item)
{
contents.push_back(item);
}
std::vector<T> contents;
};
I know how to overload operator[] as follows :
T& operator [](int idx) {
return TheArray[idx];
}
T operator [](int idx) const {
return TheArray[idx];
}
But what I want is to control values assigned by arr[i] = value.
I want to control value to be between 0 and 9.
Is there any syntax to do so?
You would have to write a template class that holds a reference to the element in the array (of type T), in this template you implement the assignment operator, and there you can implement your check. Then you return an object of this template class from your [] operator.
Something like this:
template< typename T> class RangeCheck
{
public:
RangeCheck( T& dest): mDestVar( dest) { }
RangeCheck& operator =( const T& new_value) {
if ((0 <= new_value) && (new_value < 9)) { // <= ??
mDestVar = new_value;
} else {
... // error handling
}
return *this;
}
private:
T& mDestVar;
};
Rene has provided a good answer. In addition to this, here is a full example. Note that I added a "user-defined conversion", i.e., operator T, in the proxy_T class.
#include <iostream>
#include <array>
#include <stdexcept>
template <class T>
class myClass
{
std::array<T, 5> TheArray; // Some array...
class proxy_T
{
T& value; // Reference to the element to be modified
public:
proxy_T(T& v) : value(v) {}
proxy_T& operator=(T const& i)
{
if (i >= 0 and i <= 9)
{
value = i;
}
else
{
throw std::range_error(std::to_string(i));
}
return *this;
}
operator T() // This is required for getting a T value from a proxy_T, which make the cout-lines work
{
return value;
}
};
public:
proxy_T operator [](int const idx)
{
return TheArray.at(idx);
}
T operator [](int const idx) const
{
return TheArray[idx];
}
};
int main() {
myClass<int> A;
std::cout << A[0] << std::endl;
A[0] = 2;
std::cout << A[0] << std::endl;
A[1] = 20;
}
I want to use macros to create my source code simpler
This is my code
constexpr auto make_index_sequence_array(size_t i ,std::index_sequence<arg...>) {
return std::array<size_t, sizeof...(arg)> {i+arg...};
}
#define RANGE(start , end) make_index_sequence_array(start,std::make_index_sequence<end-start>{})
#define ZeroTo(end) RANGE(0 , end)
#define Repeat(end) for(auto itr : RANGE(0 , end))
void main() {
vector<int> a = { 1,2,3 };
for (auto row : RANGE(0,a.size()))
cout << std::setw(4) << a[row];
cout << '\n';
}
I know I can use :
void main() {
vector<int> a = { 1,2,3 };
for (auto itr: a)
cout << std::setw(4) << itr;
cout << '\n';
}
But it is a simple example and I want to use this style in more situations.
The error while compiling is:
Error C2975 '_Size': invalid template argument for 'std::make_index_sequence', expected compile-time constant expression
How can I use my macros? Or even is it possible?
My recommendation is that you use iterators, I wrote an example below. Something like this would do it in C++ < C++17:
#include <iostream>
template <class T>
struct RangeIter {
RangeIter(T from, T to, T curr ) :
_from(from), _to(to), _curr(curr) {
}
T operator*() const {
return _curr;
}
T operator++() {
++_curr;
return _curr;
}
bool operator==(const RangeIter & other) {
assert(_from == other._from && _to == other._to);
return _curr == other._curr;
}
bool operator!=(const RangeIter & other) {
return !(_curr == other._curr);
}
T _from, _to, _curr;
};
template <class T>
struct Range {
Range(T from, T to) : _from(from), _to(to) {}
RangeIter<T> begin() { return RangeIter<T>(_from, _to, _from); }
RangeIter<T> end() {
return RangeIter<T>(_from, _to, _to);
}
T _from, _to;
};
template <class T>
Range<T> makeRange(T to, T from) {
return Range<T>(to, from);
}
int main() {
for (auto i : makeRange(0, 10)) {
std::cout << i << std::endl;
}
}
For C++17 you can use different types for the begin and end iterator and improve on this. You can use sentinels. You could take a look here: How the new range-based for loop in C++17 helps Ranges TS?
A C++-17 only solution here:
#include <iostream>
template <class T>
struct RangeSentinel {
RangeSentinel(T stopVal) : _stopVal(stopVal) {}
T _stopVal;
};
template <class T>
struct RangeIter {
RangeIter(T from, T to, T curr) :
_from(from), _to(to), _curr(curr) {
}
T operator*() const {
return _curr;
}
T operator++() {
++_curr;
return _curr;
}
bool operator==(const RangeSentinel<T> & other) {
assert(_from == other._from && _to == other._to);
return _curr == other._stopVal;
}
bool operator!=(const RangeSentinel<T> & other) {
return !(_curr == other._stopVal);
}
T _from, _to, _curr;
};
template <class T>
struct Range {
Range(T from, T to) : _from(from), _to(to) {}
RangeIter<T> begin() { return RangeIter<T>(_from, _to, _from); }
RangeSentinel<T> end() {
return RangeSentinel<T>(_to);
}
T _from, _to;
};
template <class T>
Range<T> makeRange(T to, T from) {
return Range<T>(to, from);
}
int main() {
for (auto i : makeRange(0, 10)) {
std::cout << i << std::endl;
}
}
As you can see, in the C++17 solution I do not need to store again _from and _to variables, since the sentinel is a different type.