How do I make it possible to call the following construct for my vector class?
vector <int> v{1,2,3};
how to properly declare such a method?
My source code:
template<typename T>
class my_vector {
T* values;
std::size_t values_num;
std::size_t max_size;
public:
explicit my_vector(std::size_t size);
~my_vector();
void push_back(const T& value);
std::size_t size();
T operator[](int pos);
T at(int pos);
};
The easy way is a constructor taking a std::initializer_list<T>.
Another way is writing a template constructor, a bit like this:
template<class...Ts> requires (std::is_same_v<T, Ts> && ...)
explicit my_vector(Ts...ts);
but getting that just right is a pain, so just go with initializer_list.
Related
I try to make a simple C++ std::list implementation and I have encountered a problem with my constructors. I have a set of constructors:
List();
List(size_t Size);
List(size_t Size, const T& Val);
List(const std::initializer_list<T>& Init);
template<typename It> List(It It1, It It2);
And a piece of code that creates an instance of the List class:
int main()
{
List<int> l(10, 9);
}
Obviously, I want to make use of this constructor:
List(size_t Size, const T& Val);
But instead it uses this constructor:
template<typename It> List(It It1, It It2);
I know that size_t is the source of the problem. If I changed size_t to int, everything would work properly.
So is there any possibility to use List(size_t Size, const T& Val); using size_t, not the template<typename It> List(It It1, It It2);?
this is my generic class Array, I would like to ask about the copy constructor and assignment, is this the right way to do it? If yes, then do I really need to insert both of them in the class?
Thank you in advance.
template <class T, int SIZE>
class Array {
T data[SIZE];
public:
explicit Array();
Array(const Array& a); //copy constructor
~Array(); //destructor
Array& operator=(const Array& a); //assignment
T& operator[](int index);
const T& operator[](int index) const;
};
template <class T,int SIZE>
Array<T,SIZE>::Array()
{
}
template <class T,int SIZE>
Array<T,SIZE>::~Array()
{
}
template <class T,int SIZE>
Array<T,SIZE>::Array& operator=(const Array& a)
{
for(int i=0;i<SIZE;i++)
{
data[i]=a[i];
}
return *this;
}
template <class T,int SIZE>
Array<T,SIZE>::Array(const Array& a)
{
for(int i=0;i<SIZE;i++)
{
data[i]=a[i];
}
}
In this case, you can apply the Rule of Zero:
template <class T, int SIZE>
class Array {
T data[SIZE];
public:
T& operator[](int index);
const T& operator[](int index) const;
};
The compiler will generate the functions for you. If you need to do something custom in them, then yes, you need to define them.
In that case, the signature of your assignment operator needs to be fixed to (i.e. your posted code does not compile):
template <class T,int SIZE>
Array<T,SIZE>& Array<T,SIZE>::operator=(const Array& a)
Then, you should be directly accessing the other array's data, instead of using its operator[] (unless you have a reason):
data[i] = a.data[i]; // note `a.data[i]` vs. `a[i]`
Moreover, you can take advantage of the compiler to avoid writing loops. Simply wrap your array into a struct:
template <class T, int SIZE>
class Array {
struct S {
T data[SIZE];
} s;
// ...
};
So that you can replace your loops with:
s = a.s;
Not only that, but using the struct will allow you to copy-construct the elements of the array, rather than copy-assigning them (as you were doing with the loop), which may be important for some T types:
template <class T,int SIZE>
Array<T,SIZE>::Array(const Array& a)
: s(a.s) // note the member initializer
{
// empty body
}
This solves the compiler error:
template <class T,int SIZE>
auto Array<T,SIZE>::operator=(const Array& a) -> Array&
{
for(int i=0;i<SIZE;i++)
{
data[i]=a[i];
}
return *this;
}
There is a separate discussion to be had about redundant initialisation of the array elements in the constructor.
This question already has answers here:
How can I write a stateful allocator in C++11, given requirements on copy construction?
(3 answers)
Closed 7 years ago.
I'm writing a custom allocator to be used with std::list. The list size will always be bounded to a small number and the list elements will be allocated and deallocated very frequently within a constraint tree search algorithm and thus I think a custom pool allocator (that allocates elements out of the stack) ought to improve performance.
My question is how does std::list allocated the data structures used to store the links/nodes. It will use the custom allocator to allocate the elements but then that wouldn't really help much if the nodes are still allocated from the heap.
This is the custom allocator I'm implementing:
#include <algorithm>
#include <cassert>
template <class Tp, std::size_t N>
class PoolStackAllocator {
public:
typedef Tp value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template<typename U>
struct rebind {
typedef PoolStackAllocator<U, N> other;
};
inline explicit PoolStackAllocator() : data_size_(0) {}
template <class U, std::size_t M>
inline explicit PoolStackAllocator(const PoolStackAllocator<U, M>& other) : data_size_(other.data_size_) {
typename PoolStackAllocator<U>::const_pointer i = other.data_;
typename PoolStackAllocator<U>::const_pointer end = other.data_ + data_size_;
pointer j = data_;
while (i != end){
*j++ = Tp(*i++);
}
j = data_ + data_size_;
pointer* k = free_;
pointer end_ = data_ + 25;
while (j != end_){
*k++ = j++;
}
}
inline pointer address(reference r) { return &r; }
inline const_pointer address(const_reference r) { return &r; }
inline pointer allocate(size_type n){
assert(n == 1);
assert(data_size_ < N);
return free_[data_size_++];
}
inline void deallocate(Tp* p, size_type n){
assert(n == 1);
free_[--data_size_] = p;
}
inline size_type max_size(){
return 1;
}
private:
size_type data_size_;
value_type* free_[N];
value_type data_[N];
};
template <class T, class U, std::size_t N>
inline bool operator==(const PoolStackAllocator<T, N>& a, const PoolStackAllocator<U, N>& b){
return &a == &b;
}
template <class T, class U, std::size_t N>
inline bool operator!=(const PoolStackAllocator<T, N>& a, const PoolStackAllocator<U, N>& b){
return &a != &b;
}
And this is an example of how I intend to use it.
typedef std::forward_list<Alien, PoolStackAllocator<Alien, 25>> Aliens;
Edit 1:
I got an answer on how std::list allocates the data structures:
In short, given allocator, we can simply do allocator::rebind::other.allocate(1) to be allocating memory large enough to hold an object U. This is the magic required for std::list to work properly, since given std::list(allocator()), std::list actually needs to allocate memory for Node, and not int. Thus, they need to rebind to allocator()::rebind >::other instead.
http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement
But now I'm still puzzled with a new question. How to implement the copy constructor.
When I create a list like:
std::forward_list<int, PoolStackAllocator<int>> ints;
This copy constructor gets called:
template <class Tp, std::size_t N>
class PoolStackAllocator {
...
template <class U, std::size_t M>
inline explicit PoolStackAllocator(const PoolStackAllocator<U, M>& other);
...
}
with
U = int
Tp = std::_Fwd_list_node<int>
I don't know what to do in this copy constructor. From the example allocaters I've seen it seems that nothing must be done there. But why? Why does it get called then?
Edit 2
Got the answer from:
How can I write a stateful allocator in C++11, given requirements on copy construction?
the container uses the same allocator to allocate nodes and memory for the stored objects.
I would like to design a vector class with small vector optimisation. It looks like:
template <typename T, int small_size = 0>
class Vector {
private:
T data_small_[small_size];
T* data_;
T* size_;
T* capacity_;
public:
...
}
Unfortunately, most of the time, the class will be used with small_size = 0. Is there a way to remove data_small_ for small_size = 0 without going into template specialisation and rewriting the whole code for the class?
You can use the empty base optimization here. You would have to change Vector to not refer to data_small_ directly. Instead, write use SmallData to implement the required member functions and handle the small_size = 0 case in the partial specialization SmallData<T, 0>. Below is an example with SmallData implementing a copy constructor and operator[] and Vector using them without worrying about small_size.
template <typename T, int small_size>
class SmallData
{
public:
SmallData(const SmallData& other)
{
for(size_t i = 0; i < small_size; i++)
data_small_[i] = other.data_small_[i];
}
T& operator[](int k){return data_small_[k];}
protected:
T data_small_[small_size];
};
template <typename T>
class SmallData<T, 0>
{
public:
T& operator[](int k){//throw some error}
};
template <typename T, int small_size = 0>
class Vector : public SmallData<T, small_size>
{
public:
Vector(const Vector& other) : SmallData<T, small_size>(other)
{
//rest of copy ctor either here on in the member init list above
}
T& operator[](int k)
{
if(k<small_size) return SmallData<T, small_size>::operator[](k);
else return data_[k];
}
};
I need to define a get method in two different ways. One for simple types T. And once for std::vector.
template<typename T>
const T& Parameters::get(const std::string& key)
{
Map::iterator i = params_.find(key);
...
return boost::lexical_cast<T>(boost::get<std::string>(i->second));
...
}
How can I specialize this method for std::vector. As there the code should look something like this:
template<typename T>
const T& Parameters::get(const std::string& key)
{
Map::iterator i = params_.find(key);
std::vector<std::string> temp = boost::get<std::vector<std::string> >(i->second)
std::vector<T> ret(temp.size());
for(int i=0; i<temp.size(); i++){
ret[i]=boost::lexical_cast<T>(temp[i]);
}
return ret;
}
But I do not know how to specialize the function for this. Thanks a lot.
Don't specialize function template.
Why Not Specialize Function Templates?
Template Specialization and Overloading
Instead, use overload.
Write a function template get_impl to handle the general case, and overload (not specialize) this to handle the specific case, then call get_impl from get as:
template<typename T>
const T& Parameters::get(const std::string& key)
{
//read the explanation at the bottom for the second argument!
return get_impl(key, static_cast<T*>(0) );
}
And here goes the actual implementations.
//general case
template<typename T>
const T& Parameters::get_impl(const std::string& key, T*)
{
Map::iterator i = params_.find(key);
return boost::lexical_cast<T>(boost::get<std::string>(i->second));
}
//this is overload - not specialization
template<typename T>
const std::vector<T>& Parameters::get_impl(const std::string& key, std::vector<T> *)
{
//vector specific code
}
The static_cast<T*>(0) in get is just a tricky way to disambiguate the call. The type of static_cast<T*>(0) is T*, and passing it as second argument to get_impl will help compiler to choose the correct version of get_impl. If T is not std::vector, the first version will be chosen, otherwise the second version will be chosen.
Erm. call it something else? e.g.
template<typename T>
const T& Parameters::getVector(const std::string& key)
{
Map::iterator i = params_.find(key);
std::vector<std::string> temp = boost::get<std::vector<std::string> >(i->second)
// T is already a vector
T ret; ret.reserve(temp.size());
for(int i=0; i<temp.size(); i++){
ret.push_back(boost::lexical_cast<typename T::value_type>(temp[i]));
}
return ret;
}
You'll have to call this as:
foo.getVector<std::vector<int> > ("some_key");
Nothing in your question precludes this.
Now, if you really do need to use get(), then you have to rely on partially specializing a structure, as function partial specialization is not supported by the language.
This is a lot more complicated, for example:
template <typename T>
struct getter
{
const T& operator()(std::string const& key)
{
// default operations
}
};
// Should double check this syntax
template <typename T>
struct getter<std::vector<T, std::allocator<T> > >
{
typedef std::vector<T, std::allocator<T> > VecT;
const VecT& operator()(std::string const& key)
{
// operations for vector
}
};
Then in you method becomes:
template<typename T>
const T& Parameters::get(const std::string& key)
{
return getter<T>()(key); // pass the structures getter needs?
}