Vector of Class Without Default Constructor

vector of class without default constructor

How can I create a std::vector of type A and give an argument to A's constructor?

std::vector<A> v1(10, 42);  // 10 elements each with value 42
std::vector<A> v2{1,2,3,4}; // 4 elements with different values

How would I add 3 to the vector?

v.emplace_back(3);          // works with any suitable constructor
v.push_back(3); // requires a non-explicit constructor

The lack of a default constructor only means you can't do operations that need one, like

vector<A> v(10);
v.resize(20);

both of which insert default-constructed elements into the vector.

Vector of objects without a default constructor and iterator

This range based loop is making copies:

for (auto i: c) {
cout << i << endl;
}

And the copy constructor initializes v to 4 (and does not make a copy):

Custom(const Custom &) : v{4} {

}

You can either implement a proper copy constructor or use references in the loop to get the desired output:

for (const auto& i: c) {
cout << i << endl;
}

I would suggest to do both, because this copy constructor is not doing a copy by any means. The compiler generated copy constructor should be fine:

Custom(const Custom &) = default;

PS: The fact that Custom has a deleted default constructor is not really relevant for the posted code. Nowhere in the code a Custom is default constructed. Also there is no iterator in your code. In the range based loop i is a copy/reference of the elements in the vector, it is not an iterator.

How can I initialize an std::array of a class without a default constructor?

With copy constructor, something along these lines:

template <typename T, size_t... Is>
std::array<T, sizeof...(Is)> MakeArrayHelper(
const T& val, std::index_sequence<Is...>) {
return {(static_cast<void>(Is), val) ...};
}

template <typename T, size_t N>
std::array<T, N> MakeArray(const T& val) {
return MakeArrayHelper<T>(val, std::make_index_sequence<N>{});
}

std::array<Foo, 100> arr = MakeArray<Foo, 100>(Foo(5));

Actually, this can be done without copy constructor after all. This solution relies heavily on C++17's mandatory copy elision.

template <typename T, size_t... Is, typename... Args>
std::array<T, sizeof...(Is)> MakeArrayHelper(
std::index_sequence<Is...>, Args&&... args) {
return {(static_cast<void>(Is), T{std::forward<Args>(args)...}) ...};
}

template <typename T, size_t N, typename... Args>
std::array<T, N> MakeArray(Args&&... args) {
return MakeArrayHelper<T>(std::make_index_sequence<N>{},
std::forward<Args>(args)...);
}

Demo

std::vector works with classes that are not default constructible?

The requirement in C++03 is that types being stored in a container be CopyConstructible and Assignable (see §23.1 Container Requirements). However, in C++11 these requirements are relaxed, and tend to apply to the operations performed on the container. So a simple default construction has no requirements (see teble 96, §23.1 in C++11 standard).

As soon as you try to copy a vector, or insert elements into it, you will meet the CopyInsertable, CopyAssignable, EmplaceConstructible, MoveInsertable, MoveAssignable etc. requirements

Is there still a need to provide default constructors to use STL containers?

This quote is from the C++ Programming Language, Special edition , 2005 by Bjarne Stroustrup in section 16.3.4:

If a type does not have a default constructor, it is not possible to create a vector with elements of that type, without explicitly providing the value of each element.

So it was indeed a standard requirement. It was also required that (section 17.1.4) :

To be an element of a container, an object must be of a type that allows the container implementation to copy it. The container may copy it using a copy constructor or an assignment; in either case the result of the copy must be an equivalent object.

So yes, there were "official" constructor requirementsand the library implementation were supposed to be interchangeable and not add other requirements. (Already in the very first proposal for STL in 1995, the authors tried as much as possible to clearly indicate specifications and narrow down the implementation dependent flexibility.)

You therefore had to provide a default constructor in the case where you declared other constructors:

If a user has declared a default constructor, that one will be used; otherwise, the compiler will try to generate one if needed and if the user hasn't declared other constructors.

Nowadays, this requirement is relaxed. Since C++11:

The requirements that are imposed on the elements depend on the actual operations performed on the container.

So you can define a vector for a class without default constructor, if it doesn't make sense. This for example perfectly works (online demo):

class MyClass {
public:
MyClass(int x) {}
};
int main() {
vector<MyClass> v;
MyClass test{1};
v.push_back(test);
}

But it works only as long as you don't use any operation that would need the default constructor. For instance v.resize(6); would fail to compile.

stl vector and c++: how to .resize without a default constructor?

Use the 2-argument overload:
many_things.resize(20, something(5));

No default constructor exists for class if I want to construct a class with another one

The problem is that since you have a parameterized constructor for your class Ball, the compiler will not synthesize a default constructor by itself. So if you want to create/construct an object of class Ball using a default constructor, say by writing Ball b;, then you must first provide a default constrcutor for class Ball as shown below.

Solution 1

Just add a default constrctor in class Ball as shown below:

Ball.h

class Ball {
private:
Vector3 position;
Vector3 speed;
float radius = 0;//use in-class initializer
public:
Ball(Vector3 ballPosition, Vector3 ballSpeed,float ballRadius);
Ball() = default; //DEFAULT CONSTRUCTOR ADDED
};

Alternative Method of adding constructor

Another way of adding a default constructor would be as shown below:

Ball.h

#pragma once
#include <vector>
#include <raylib.h>

class Ball {
private:
Vector3 position;
Vector3 speed;
float radius = 0;//use IN-CLASS INITIALIZER
public:
Ball(Vector3 ballPosition, Vector3 ballSpeed,float ballRadius);
Ball(); //declaration for default constrctor
};

Ball.cpp

#include "Ball.h"
#include <raymath.h>
#include <rlgl.h>

Ball::Ball(Vector3 position, Vector3 speed,float radius) {
this->position = position;
this->speed = speed;
this->radius = radius;

}
//define the default constructor
Ball::Ball()
{
cout << "default constructor" << endl;
//do other things here if needed
}


Related Topics



Leave a reply



Submit