Disable the std::initializer_list constructor - c++

I am trying to work out if it is possible to disable the std::initializer_list constructor in certain circumstances. I am writing a custom vector class that support expressions (math and relational operators). The relational expression implicitly converts to bool to allow use in an if-statement but this is causing me problems with the constructors for my vector class (v5) in some cases where I want a vector result from the relational expression.
#include <vector>
struct expr
{
std::size_t size() const { return 1; }
auto at(std::size_t i) const { return 1.0; }
operator bool() const { return true; }
};
template<typename T>
struct my_vec
{
my_vec(std::initializer_list<T> init)
: vec_{ init }
{}
my_vec(std::size_t sz)
: vec_( sz )
{}
my_vec(expr e)
: vec_( e.size() )
{
for (std::size_t i = 0; i != e.size(); ++i) vec_.at(i) = e.at(i);
}
std::vector<T> vec_;
};
void test_init_a(void)
{
// initialize_list constructor
my_vec<double> v1{ 1.0 };
my_vec<double> v2{ 1.0, 2.0 };
// size_t constructor
my_vec<double> v3(5);
// expression constructors
expr e;
my_vec<double> v4( e );
my_vec<double> v5{ e }; // <-- this one attempts the initializer_list constructor because of the implicit cast to bool and fails due to narrowing
}
I could just always use the parenthesis to construct my vector from an expression (v4) but I wanted to figure out it it is possible to disable an initializer_list constructor at all?
I have tried is to implement a wrapper to the initializer_list and use double brace initialization, however this then causes single-element access a problem (v1) as it tries to use the size_t constructor instead of the initializer_list constructor.

I wanted to figure out it it is possible to disable an initializer_list constructor at all?
You can't. This is how the language works and if you used braced initialization and you have a std::initializer_list constructor then that is one that is called.
What you can do though is remove the implicit conversion that is allowing the std::initializer_list to be created in the first place. If you make expr::operator bool() explicit then e can't be converted to bool which means the only suitable overload is now my_vec(expr e)

As a workaround, you can make the constructor template, then apply SFINAE to make it unusable when the type in std::initializer_list is not same as the template parameter T of the class.
template <typename X, std::enable_if_t<std::is_same_v<X, T>>* = nullptr>
my_vec(std::initializer_list<X> init)
: vec_{ init }
{}
LIVE

Related

Emplacement-like construction for std::vector

Imagine I want to construct a fixed-size std::vector of objects without move or copy constructors, such as std::atomic<int>. In this case the underlying std::atomic class has a 1-arg constructor which takes an int, as well as a default constructor (which initializes the value to 0).
Using the initializer_list syntax like std::vector<std::atomic<int>> v{1,2,3} doesn't work, because the arguments are first converted to the element type T of the vector as part of the creation of the initializer_list and so the copy or move constructor will be invoked.
In the particular case of std::atomic<int> I can default-construct the vector and then mutate the elements after:
std::vector<std::atomic<int>> v(3);
v[0] = 1;
v[1] = 2;
v[2] = 3;
However, in addition to being ugly and inefficient, it isn't a general solution since many objects may not offer post-construction mutation equivalent to what you could get by calling the appropriate constructor.
Is there any way to get the "emplace-like" behavior that I want at vector construction?
A general solution is to make your vector take a custom allocator whose construct method performs the appropriate initialization. In the code below, v uses the MyAllocator<NonMovable> allocator rather than std::allocator<NonMovable>. When the construct method is called with no arguments, it actually calls the constructor with the appropriate argument. In this way, the default constructor can initialize the elements properly.
(For simplicitly, I have made next_value static in this example, but it could just as well be a non-static member variable that's initialized when MyAllocator is constructed.)
#include <stdio.h>
#include <memory>
#include <new>
#include <vector>
struct NonMovable {
NonMovable(int x) : x(x) {}
const int x;
};
template <class T>
struct MyAllocator {
typedef T value_type;
static int next_value;
T* allocate(size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, size_t n) {
::operator delete(p);
}
template <class U>
void construct(U* p) {
new (p) U(++next_value);
}
};
template <class T> int MyAllocator<T>::next_value = 0;
int main() {
std::vector<NonMovable, MyAllocator<NonMovable>> v(10);
for (int i = 0; i < 10; i++) {
printf("%d\n", v[i].x);
}
}
http://coliru.stacked-crooked.com/a/1a89fddd325514bf
This is the only possible solution when you're not allowed to touch the NonMovable class and its constructor may require multiple arguments. In the case where you only need to pass one argument to each constructor, there is a much simpler solution that uses the range constructor of std::vector, like so:
std::vector<int> ints(10);
std::iota(ints.begin(), ints.end(), 1);
std::vector<NonMovable> v(ints.begin(), ints.end());
(Though if you can't afford the extra memory, then you'll have to write a custom iterator, which will be a lot more code.)

why does range-v3 yield require default constructor

I am trying to understand, for what reasons does the yield family of functions require that class be default constructible?
In the following example, the vnums1 line compiles only if CNum has a default constructor. The vnums2 line does not require a default constructor.
I am using Visual Studio 2017 and Range-V3-VS2015. Thank you!
#include <range/v3/all.hpp>
struct CNum
{
// CNum() = default;
explicit CNum(int num) : m_num(num) {}
int m_num;
};
int main()
{
auto ints = ranges::view::ints(0, 10);
// this compiles only of CNum has a default constructor
auto vnums1 = ints
| ranges::view::for_each([](int num) { return ranges::yield_if(num % 2, CNum(num)); })
| ranges::to_vector;
// this compiles even if CNum does not have a default constructor
auto vnums2 = ints
| ranges::view::remove_if([](int num) { return num % 2 == 0; })
| ranges::view::transform([](int num) { return CNum(num); })
| ranges::to_vector;
return 0;
}
We just changed the code to not require DefaultConstructible. git pull and enjoy.
The reason you need to default constructor to use ranges::yield_if is that the machinery it uses requires the type to be default constructable. If we look at the code we have
struct yield_if_fn
{
template<typename V>
repeat_n_view<V> operator()(bool b, V v) const
{
return view::repeat_n(std::move(v), b ? 1 : 0);
}
};
/// \relates yield_if_fn
/// \ingroup group-views
RANGES_INLINE_VARIABLE(yield_if_fn, yield_if)
And we can see that it calls view::repeat_n. Looking at that code we get
repeat_n_view<Val> operator()(Val value, std::ptrdiff_t n) const
{
return repeat_n_view<Val>{std::move(value), n};
}
And if we look at repeat_n_view we have
// Ordinarily, a view shouldn't contain its elements. This is so that copying
// and assigning ranges is O(1), and also so that in the event of element
// mutation, all the copies of the range see the mutation the same way. The
// repeat_n_view *does* own its lone element, though. This is OK because:
// - O(N) copying is fine when N==1 as it is in this case, and
// - The element is immutable, so there is no potential for incorrect
// semantics.
struct repeat_n_view
: view_facade<repeat_n_view<Val>, finite>
{
private:
friend range_access;
Val value_;
std::ptrdiff_t n_;
// ...
public:
repeat_n_view() = default;
constexpr repeat_n_view(Val value, std::ptrdiff_t n)
: value_(detail::move(value)), n_((RANGES_EXPECT(0 <= n), n))
{}
constexpr std::size_t size() const
{
return static_cast<std::size_t>(n_);
}
};
We see from the comment that this was a design decision and because of this design you need your type to be default constructable. Eric describes the type required as being SemiRegular which is documented as
it needs to be default constructable, copy and move constructable, and destructable.

Populate vector with integer sequence, on construction

I am trying to populate a vector (or other container) with a sequence of integers on construction of the vector (contrasting to this question). The following code does what I intend, using the range constructor for a vector, and Boost's counting_range:
#include <iostream>
#include <vector>
#include <boost/range/counting_range.hpp>
using namespace std;
int main () {
vector<int> test_vector(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end());
for (auto i : test_vector) cout << i << endl;
}
Questions:
Can I eliminate the duplication in counting_range(2,10).begin() and counting_range(2,10).end()? Currently I'm specifying the range of (2,10) twice.
Can this be done without Boost, using just vanilla C++11 or C++0x? Edit: or C++14?
Edit:
I'd like to instantiate the vector and specify the range all in a single statement. For example in Python I could write test_vector=range(2,9). In R/Octave/Matlab one can write test_vector=2:9 or test_vector=seq(2,9,1). In this regard I'm satisfied with what I have above.
I used "2" and "10" above, but boundary of the range could be dynamic, any integers a,b in scope with a≤b. So an initialization list like {2,3,...,9} isn't desirable since it must be specified at compile-time.
The method I've used above can also be used to initialize other containers in a single statement; the following also works:
unordered_set test_set(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end());
It would be nice if any solution was also as 'container independent'.
I agree with #david-rodriguez-dribeas that we can adhere to Meyer's Item by initializing the vector in the constructor's body; it isn't necessary for it to be done in the constructor's initialization list. So I have struck-out this portion below.
Motivation:
I want to do this is because I want to obey Meyer's Item 4 ("Make sure objects are initialized before they are used.") in Effective C++ elsewhere in my code. For example:
class my_class {
public:
my_class()
:vec(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end()) {}
vector<int> vec;
};
Don't over do it. The simple thing to do is to default initialize the vector, reserve and initialize from the range.
Make sure objects are initialized before they are used.
That does not mean that the member must be fully initialized in the initialization list, rather than the my_class object must be fully initialized when the constructor completes.
Other than that, just for the sake of it, there are different things you can do in vanilla C++ to handle this, like creating a helper function and returning the vector by value:
std::vector<int> create_vector() {
std::vector<int> v;
// ...
return v;
}
But I would not use this (or any other alternative) to initialize a member, only if needed (the vector is const might be sufficient excuse :))
You can constructor std::vector<T> with a sequence delimited by ranges. You just need a suitable input iterator to initialize the sequence and you can define your iterator such that you can use a default constructed iterator for the end, e.g.:
class counter: public std::iterator<std::input_iterator_tag, int> {
int current;
int end;
public:
counter(): current(), end() {}
counter(int c, int e): current(c), end(e) {}
int const& operator*() const { return this->current; }
counter& operator++() { ++current; return *this; }
counter operator++(int) { counter rc(*this); ++current; return rc; }
bool operator== (counter const& other) const {
return (end - current) == (other.end - other.current);
}
bool operator!= (counter const& other) const { return !(*this == other); }
};
std::vector<int> v(counter(2, 10), counter());
The Boost way to do this is to use boost::copy_range:
auto vec = boost::copy_range<std::vector<int>>(boost::irange(0, 10));
boost::copy_range is a function template that returns an object of its first template parameter type:
template< typename SeqT, typename Range >
inline SeqT copy_range( const Range& r )
{
return SeqT( boost::begin( r ), boost::end( r ) );
}
You do have to specify the return type as a template parameter, which can violate DRY but can be avoided using AAA style, as in my example above, or using decltype in a class initializer:
class S {
public:
S() : vec(boost::copy_range<decltype(vec)>(boost::irange(0, 10)) {}
private:
const std::vector<int> vec;
};
This has never failed anyone, and it's completely vanilla, even works with C++03 (provided you backport begin, end, like I did; if not, just use compile-time array size):
int inits[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> test_vector ( begin(inits), end(inits) );
If the sequence is another kind of sequence, you only need to adapt your initialization array for it, or use a generator object, and bam! It's done.
As for avoid the duplication in (1), what's wrong if anything with the following?
const boost::counting_range init_range(2,10);
vector<int> test_vector( begin(init_range), end(init_range) );
Instead of creating boost::counting_range, you can use directly boost::counting_iterator to initialize your sequence.
#include <iostream>
#include <vector>
#include <boost/iterator/counting_iterator.hpp>
int main ()
{
std::vector<int> aVec(boost::counting_iterator<int>(0), boost::counting_iterator<int>(10));
for (int i : aVec)
{
std::cout << i << std::endl;
}
return 0;
}

Container of fixed dynamic size

Is there a standard container for a sequence of fixed length, where that length is determined at runtime. Preferrably, I'd like to pass an argument to the constructor of each sequence element, and use that argument to initialize a const member (or a reference). I'd also like to obtain the sequence element at a given index in O(1). It seems to me that all of my requirements cannot be met at the same time.
I know std::array has fixed length, but that length has to be known at compile-time.
std::vector has dynamic size, and allows passing contructor arguments using emplace. Although you can reserve memory to avoid actual reallocations, the type still has to be movable to theoretically allow such reallocations, which e.g. prevents const members.
Then there is std::list and std::forward_list, which don't require a movable type, but which are still resizable and will perform rather poorly under random-access patterns. I also feel that there might be considerable overhead associated with such lists, since each list node will likely be allocated separately.
Strangely enough, std::valarray is my best bet so far, since it has a fixed length and won't resize automatically. Although there is a resize method, your type won't have to be movable unless you actually call that method. The main deficit here is the lack for custom constructor arguments, so initializing const members isn't possible with this approach.
Is there some alternative I missed? Is there some way to adjust one of the standard containers in such a way that it satisfies all of my requirements?
Edit: To give you a more precise idea of what I'm trying to do, see this example:
class A {
void foo(unsigned n);
};
class B {
private:
A* const a;
const unsigned i;
public:
B(A* aa) : a(aa), i(0) { }
B(A* aa, unsigned ii) : a(aa), i(ii) { }
B(const std::pair<A*, unsigned>& args) : B(args.first, args.second) { }
B(const B&) = delete;
B(B&&) = delete;
B& operator=(const B&) = delete;
B& operator=(B&&) = delete;
};
void A::foo(unsigned n) {
// Solution using forward_list should be guaranteed to work
std::forward_list<B> bs_list;
for (unsigned i = n; i != 0; --i)
bs_list.emplace_front(std::make_pair(this, i - 1));
// Solution by Arne Mertz with single ctor argumen
const std::vector<A*> ctor_args1(n, this);
const std::vector<B> bs_vector(ctor_args1.begin(), ctor_args1.end());
// Solution by Arne Mertz using intermediate creator objects
std::vector<std::pair<A*, unsigned>> ctor_args2;
ctor_args2.reserve(n);
for (unsigned i = 0; i != n; ++i)
ctor_args2.push_back(std::make_pair(this, i));
const std::vector<B> bs_vector2(ctor_args2.begin(), ctor_args2.end());
}
Theoretically vector has the properties you need. As you noted, actions that possibly do assignments to the contained type, including especially any sequence modifications (empace_back, push_back, insert etc.) are not supported if the elements are noncopyable and/or nonassignable. So to create a vector of noncopyable elements, you'd have to construct each element during vector construction.
As Steve Jessop points out in his answer, if you define the vector const in the first place you won't even be able to call such modifying actions - and of course the elements remain unchanged as well.
If I understand correctly, you have only a sequence of constructor arguments, not the real object sequence. If it's only one argument and the contained type has a corresponding constructor, things shoule be easy:
struct C
{
const int i_;
C(int i) : i_(i) {}
};
int main()
{
const std::vector<C> theVector { 1, 2, 3, 42 };
}
If the constructor is explicit, you have to make a list first or explicitly construct the objects in the initializer-list:
int main()
{
auto list = { 1, 2, 3, 4 };
const std::vector<C> theVector (std::begin(list), std::end(list));
const std::vector<C> anotherVector { C(1), C(44) };
}
If it's more than just one argument per constructed object, consider a intermediate creator object:
struct C
{
const int i_;
C(int i, int y) : i_(i+y) {}
};
struct CCreator
{
int i; int y;
explicit operator C() { return C(i,y); }
};
int main()
{
const std::vector<CCreator> ctorArgs = { {1,2}, {3,42} };
const std::vector<C> theVector { begin(ctorArgs), end(ctorArgs) };
}
I think const std::vector<T> has the properties you ask for. Its elements aren't actually defined with const, but it provides a const view of them. You can't change the size. You can't call any of the member functions that need T to be movable, so for normal use they won't be instantiated (they would be if you did an extern class declaration, so you can't do that).
If I'm wrong, and you do have trouble because T isn't movable, try a const std::deque<T> instead.
The difficulty is constructing the blighter -- in C++11 you can do this with an initializer list, or in C++03 you can construct a const vector from a non-const vector or from anything else you can get iterators for. This doesn't necessarily mean T needs to be copyable, but there does need to be a type from which it can be constructed (perhaps one you invent for the purpose) .
Add a level of indirection by using a std::shared_ptr. The shared pointer can be copied and assigned as usual, but without modifying the object that is pointed to. This way you should not have any problems, as the following example shows:
class a
{
public:
a(int b) : b(b) { }
// delete assignment operator
a& operator=(a const&) = delete;
private:
// const member
const int b;
};
// main
std::vector<std::shared_ptr<a>> container;
container.reserve(10);
container.push_back(std::make_shared<a>(0));
container.push_back(std::make_shared<a>(1));
container.push_back(std::make_shared<a>(2));
container.push_back(std::make_shared<a>(3));
Another advantage is the function std::make_shared which allows you to create your objects with an arbitrary number of arguments.
Edit:
As remarked by MvG, one can also use std::unique_ptr. Using boost::indirect_iterator the indirection can be removed by copying the elements into a new vector:
void A::foo(unsigned n)
{
std::vector<std::unique_ptr<B>> bs_vector;
bs_vector.reserve(n);
for (unsigned i = 0; i != n; ++i)
{
bs_vector.push_back(std::unique_ptr<B>(new B(this, i)));
}
typedef boost::indirect_iterator<std::vector<std::unique_ptr<B>>::iterator> it;
// needs copy ctor for B
const std::vector<B> bs_vector2(it(bs_vector.begin()), it(bs_vector.end()));
// work with bs_vector2
}
I also encounter this problem, the use case in my code is to provide a thread-safe vector, the elements number is fixed and are atomic numbers. I have read all the great answers here. I think we may also consider my solution:
Just inherited the std::vector and hide the modifiers such as push_back, emplace_back, erase, then we get a fixed size vector. We can only access and modify the elements with operator [].
template <typename T>
class FixedVector : protected std::vector<T> {
public:
using BaseType = std::vector<T>;
FixedVector(size_t n) : BaseType(n) {}
FixedVector(const T &val, size_t n) : BaseType(val, n) {}
typename BaseType::reference operator[](size_t n) {
return BaseType::operator[](n);
}
};

Why can't you put a const object into a STL container?

See the code below - I am trying to put a const object into a vector. I know the answer is "STL containers require objects to be assignable and copy constructable", but, without citing the standard, can anyone explain what the problem with doing this is? I don't understand why a class like this could not be copied (besides that c++ doesn't allow it).
All it is is a value stored that is not allowed to be changed - why can't putting it in a vector simply create another one of these objects?
#include <vector>
// Attempt 1
// /home/doriad/Test/Test.cxx:3:8: error: non-static const member ‘const int MyClass::x’, can’t use default assignment operator
// struct MyClass
// {
// int const x;
// MyClass(int x): x(x) {}
// };
//
// int main()
// {
// std::vector<MyClass> vec;
// vec.push_back(MyClass(3));
// return 0;
// }
// Attempt 2
// /home/doriad/Test/Test.cxx:28:23: error: assignment of read-only member ‘MyClass::x’
struct MyClass
{
int const x;
MyClass(int x): x(x) {}
MyClass& operator= (const MyClass& other)
{
if (this != &other)
{
this->x = other.x;
}
return *this;
}
};
int main()
{
std::vector<MyClass> vec;
vec.push_back(MyClass(3));
return 0;
}
EDIT:
It is possible to do this with std::set and std::list. I guess it is the sort() function in std::vector that uses assignment. This is not UB right?
#include <set>
// Attempt 1
struct MyClass
{
int const x;
MyClass(int x): x(x) {}
bool operator< (const MyClass &other) const;
};
bool MyClass::operator<(const MyClass &other) const
{
if(this->x < other.x)
{
return true;
}
else if (other.x < this->x)
{
return false;
}
}
int main()
{
std::set<MyClass> container;
container.insert(MyClass(3));
return 0;
}
EDIT2: (Removing a bunch of stuff that doesn't have to work) The C++11 standard states that the insert method for vector and deque (and the default implementation of push_back for that matter) requires the value type to be CopyAssignable, i.e., the value supports:
t= v;
Classes and structs with const members are not CopyAssignable by default, so what you want to do won't work.
This doc (n3173) has an explanation for the various container requirements.
One possible solution would be to store pointers to the objects in the vector, because pointers are assignable and copy constructable.
Another possible solution would be to declare x without the const keyword, but ensure that it cannot be modified through encapsulation (i.e. you should declare it as private and don't modify from anywhere outside the constructor)..
When you place an object of type MyClass in the std::vector, the vector will make a copy of the object for storage, and not the object you passed to it.