The template-class gsl::vector is one of two descendants of gsl::v_iface.
The other is gsl::vector_view.
gsl::vector exists to provide constructors for gsl::v_iface.
- gsl::vector provides no interface other than construction.
- gsl::v_iface provides almost all of the interfaces that GSL's native vector provides.
- Any instance constructed via one of the template-specializations of gsl::vector owns memory allocated both for the elements in the vector and for some metadata referring to that memory.
Dynamic Allocation of Memory
For any element-type T
(double
, float
, complex<long double>
, etc.), any instance of gsl::vector<T,0>
has the number of elements determined at run-time.
- Internally, construction involves a call
- to
gsl_vector_alloc()
or
- to
gsl_vector_calloc()
.
- The pointer returned is stored on the stack inside the instance.
- There is also on the stack a pointer to the virtual-function table.
- The ultimate base-class gsl::v_stor<T>, which manages the pointer, has a virtual destructor.
Static Allocation of Memory
For any element-type T
and positive compile-time integer N
, any instance of gsl::vector<T,N>
has the number of elements determined at compile-time.
- Internally, the elements are allocated on the stack in a C-style array.
- The return-value of
gsl_vector_view_array()
is also stored on the stack in the instance of gsl::vector.
- The instance of
gsl_vector_view
returned by gsl_vector_view_array()
consumes about 40 bytes on a 64-bit machine.
- There is on the stack no pointer to the virtual-function table.
- There is no virtual destructor in the inheritance-chain.
Example
The template-parameters to gsl::vector often need not be specified because they are deduced by the compiler.
Here is an example-program:
using std::cout;
using std::endl;
int main() {
complex<float> const a1[]= {{1.0f, -1.0f}, {0.5f, 0.6f}};
vector v1= a1;
cout << "\n"
<< "v1 has, on stack, elements and internal vector-view.\n"
<< "Instance of gsl_vector_view consumes 40 bytes on 64-bit hardware.\n"
<< "sizeof(v1)=" << sizeof(v1) << " v1=" << v1 << "\n"
<<
"v1.get(1)=" << v1.
get(1) << endl;
vector v2({1.0, 3.0, 5.0, 7.0, 9.0});
cout << "\n"
<< "v2 also has, on stack, elements and internal vector-view.\n"
<< "sizeof(v2)=" << sizeof(v2) << " v2=" << v2 << "\n"
<< "v2.max()=" << v2.max() << endl;
cout << "\n"
<< "v3 has, on stack, pointer to vtable and pointer to gsl_vector.\n"
<< "Pointer to vtable is stored because dynamic vector (with compile-\n"
<< "time size of 0) has virtual destructor.\n"
<< "sizeof(v3)=" << sizeof(v3) << " v3=" << v3 << "\n"
<< "v3.isnull()=" << v3.isnull() << endl;
float a4[]= {1, 2, 3, 4, 5, 6};
vector v4(a4, 4);
cout << "\n"
<< "v4 also has, on stack, ptr to vtable and ptr to gsl_vector.\n"
<< "sizeof(v4)=" << sizeof(v4) << " v4=" << v4 << "\n"
<< "v4.subvector(2, 1, 2)=" << v4.subvector(2, 1, 2) << endl;
short a5[]= {1, 2, 3, 4, 5, 6};
vector v5(a5, 3, 2);
cout << "\n"
<< "v5 also has, on stack, ptr to vtable and ptr to gsl_vector.\n"
<< "sizeof(v5)=" << sizeof(v5) << " v5=" << v5 << "\n"
<< "v5.max_index()=" << v5.max_index() << endl;
}
And here is its output:
v1 has, on stack, elements and internal vector-view.
Instance of gsl_vector_view consumes 40 bytes on 64-bit hardware.
sizeof(v1)=56 v1=[(1,-1),(0.5,0.6)]
v1.get(1)=(0.5,0.6)
v2 also has, on stack, elements and internal vector-view.
sizeof(v2)=16 v2=[1,3,5,7,9]
v2.max()=9
v3 has, on stack, pointer to vtable and pointer to gsl_vector.
Pointer to vtable is stored because dynamic vector (with compile-
time size of 0) has virtual destructor.
sizeof(v3)=16 v3=[0,0,0,0]
v3.isnull()=1
v4 also has, on stack, ptr to vtable and ptr to gsl_vector.
sizeof(v4)=16 v4=[1,2,3,4]
v4.subvector(2, 1, 2)=[2,4]
v5 also has, on stack, ptr to vtable and ptr to gsl_vector.
sizeof(v5)=16 v5=[1,3,5]
v5.max_index()=2
Move-Construction
The move-constructor is also defined for gsl::vector<T>
, and so one could also do something like this:
#include <gsl/vector.hpp>
vector<float,0> read_big_file_into_array();
int main() {
vector v= read_big_file_into_array();
return 0;
}
In compiling the code above, the compiler,
- might elide the constructor-call in the installation of the return-value from
read_big_file_into_array()
into v
, but
- is not required to elide the call (according to C++-17), depending on how the vector inside the function is obtained.
However, even if the compiler do not elide the call to a constructor, the big array would still not be copied into v
because there is a move-constructor that would be called.