vector<bool> raises an error on const data() method - c++

I have the following code:
#include <vector>
struct TestStruct {
std::vector<float> float_vect;
std::vector<bool> bool_vect;
};
void func(const TestStruct & test)
{
const float * p1 = test.float_vect.data(); //<--- this line works fine
const bool * p2 = test.bool_vect.data(); //<--- on this line error happens
}
int main()
{
TestStruct test;
func(test);
}
Error message:
passing 'const std::vector' as 'this' argument of 'void std::vector::data() [with _Alloc = std::allocator]' discards qualifiers [-fpermissive]
data() method of std::vector have const specified.
Why this method works fine on float vector, and raises an error on boolean vector ?

vector<bool> is a specialization of a good old vector<T> and it may be implemented differently from ordinary vector (e.g. some space-saving optimizations may be employed). Side-effect of such design is that it does not always behave as ordinary vector (many consider vector<bool> to be broken because of that).
For example, the reference at http://en.cppreference.com/w/cpp/container/vector_bool does not mention vector<bool>::data() at all. Therefore - you should not use it when using vector with type bool. The fact that you don't get an error similar to method not found is - in your case - just a matter of how vector<bool> is implemented by your compiler.

std::vector<bool> is a template specialization of the class std::vector<T> in the STL. In order to use less memory, the boolean values are stored by 8 in a byte. It means there is no direct data accessible as you expect because the class doesn't store it in the same way than for other types.
Look at the doc :
The specialization has the same member functions as the unspecialized vector, except data, emplace, and emplace_back, that are not present in this specialization.

Related

Using constexpr vectors in template parameters (C++20)

Recently, I've been playing around with C++20's new constexpr std::vectors (I'm using GCC v12), and have run into a slight problem (this is actually an extension of my last question, but I thought it would just be better to make a new one). I've been trying to use constexpr std::vectors as members to a class, but this seems like it doesn't work as you cannot annotate them with constexpr and therefore constexpr functions think they can't be evaluated at compile time, so now I am trying to use template parameters instead, like this:
#include <array>
template<int N, std::array<int, N> arr = {1}>
class Test
{
public:
constexpr int doSomething()
{
constexpr const int value = arr[0];
return value * 100;
}
};
int main()
{
Test<10> myTestClass;
// return the value to prevent it from being optimized away
return myTestClass.doSomething();
}
This results in the expected assembly output (simply returning 100 from main):
main:
mov eax, 100
ret
However, something like this doesn't work for std::vectors, even though they can be constexpr now!
#include <vector>
template<std::vector<int> vec = {1}>
class Test
{
public:
constexpr int doSomething()
{
constexpr const int value = vec[0];
return value * 100;
}
};
int main()
{
Test<> myTestClass;
return myTestClass.doSomething();
}
This throws this error:
<source>:3:35: error: 'std::vector<int>' is not a valid type for a template non-type parameter because it is not structural
3 | template<std::vector<int> vec = {1}>
| ^
In file included from /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/vector:64,
from <source>:1:
/opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/bits/stl_vector.h:423:11: note: base class 'std::_Vector_base<int, std::allocator<int> >' is not public
423 | class vector : protected _Vector_base<_Tp, _Alloc>
| ^~~~~~
<source>: In function 'int main()':
<source>:17:24: error: request for member 'doSomething' in 'myTestClass', which is of non-class type 'int'
17 | return myTestClass.doSomething();
How can I do this with vectors, or is it even possible? And, is it possible to make constexpr members, or not?
You still (in C++20 and I don't think there is any change for C++23) can't use a std::vector as a non-type template argument or have any std::vector variable marked constexpr or have any constant expression resulting in a std::vector as value at all.
The only use case that is allowed now in C++20 that wasn't allowed before is to have a (non-constexpr) std::vector variable or object which is constructed while a constant expression is evaluated and destroyed before the constant evaluation ends.
This means you can now for example take the function
int f() {
std::vector<int> vec;
vec.push_back(3);
vec.push_back(1);
vec.push_back(2);
std::sort(vec.begin(), vec.end());
return vec.front();
}
add a constexpr on it and use it in constant expression e.g.
static_assert(f() == 1);
But that's all. It is still very useful, because beforehand you could only use algorithms that don't need any dynamic memory allocation to calculate something at compile-time. That meant that you often couldn't just use the usual runtime algorithm/implementation directly in a compile-time context.
The same is true for any type that keeps references to dynamically allocated memory. You need to destroy them during the constant expression evaluation, i.e. they must be temporaries or local variables in a function or return values which are not stored in a runtime context.
In the specific case of non-type template arguments the situation is even stricter. Not all types that you could make a constexpr variable of, can be used as non-type template arguments. There are much stricter restrictions. They must be so-called structural types.
These are for example fundamental types such as arithmetic types, pointers, etc. A class type is a structural type if it is a literal type and also has only non-static data members which are public and non-mutable as well as all of them, recursively, structural types as well.
I think it is clear that std::vector doesn't satisfy these requirements. std::array is explicitly specified to be a structural type, which is why you are allowed to use it as non-type template argument.

Data member pointers as associative container keys

I am trying to create an std::set of pointers to data members. However, I can't find a method to sort or hash such pointers.
They can't be compared with operator<, they don't seem to be supported by std::less and there is no standard integer type that is guaranteed to hold their representation (they might not fit in std::uintptr_t).
This is what I tried first (https://godbolt.org/z/K8ajn3rM8) :
#include <set>
struct foo
{
int x;
int y;
};
using t_member_ptr = int (foo::*);
const std::set<t_member_ptr> members = {
&foo::x,
&foo::y
};
It produces the error error: invalid operands of types 'int foo::* const' and 'int foo::* const' to binary 'operator<'. The full error message also implies this occurs during instantiation of std::less.
I found a similar question (Set of pointer to member) but it dates back to C++14 and the answers boil down to "put your pointers in a vector and perform a linear search instead".
Has there been any changes with C++17 or C++20 that make it possible to use pointers to data members as keys for standard associative containers?
Compare them bytewise, e.g. using this comparator:
#include <cstring>
#include <type_traits>
struct BitLess
{
template <typename T>
requires std::has_unique_object_representations_v<T>
constexpr bool operator()(const T &a, const T &b) const
{
return std::memcmp(reinterpret_cast<const char *>(&a), reinterpret_cast<const char *>(&b), sizeof(T)) < 0;
}
};
Checking std::has_unique_object_representations_v<T> ensures that there's no padding inside. It tried it on GCC, Clang, and MSVC, and it returned true for member pointers on all three.

What do member types mean in vectors?

When I went through std::vector over at cppreference.com and found a section called "Member types" I did not understand what that means. In fact the member types section is present in all the reference documents of containers in stl library.
Can someone help me in understanding this?
The member types define the types that are used by the vector object. Many standard containers use member types to describe the types used, so that the programmer does not need to figure them out manually. This is particularly useful when working with complex templates, where types may be hard to determine.
For example, the return type of std::vector<___>::size() is usually std::size_t, however another C++ vector implementation may return a different integer type (e.g. int32_t). Instead of assuming what types you need in your code (and possibly introducing dangerous casting), you can simply use the member types exposed by the vector to write your code with the perfect types every time. For example:
std::vector<int> vec;
const std::vector<int>::value_type val = 4;
// std::vector<int>::value_type is a typedef of int!
vec.push_back(val);
const std::vector<int>::size_type size = vec.size();
// std::vector<int>::size_type is a typedef of std::size_t, usually.
const std::size_t size2 = vec.size();
// same as above, but we assume that vec.size() returns a size_t.
// If it does not, we may cause a narrowing conversion!
for (std::vector<int>::const_iterator it = vec.begin(), end = vec.end(); it != end; ++it)
{
// The const_iterator type is also a member type of vector.
std::cout << *it << std::endl;
}
Iterators are probably the most common usage of member types in standard containers. Rather than having to figure out if an iterator is a random access iterator, or a simple forward iterator, or any other kind of iterator, we can just use the iterator member type exposed by the container.
The C++11 auto keyword can take this even further. Instead of doing:
const std::vector<int>::size_type size = vec.size();
we can now just do:
const auto size = vec.size();
and the compiler will automatically work it out.
Generally speaking most C++ standard objects will use the same member types where possible (e.g. size_t for size_type, T for value_type, T& for reference_type), but it is not guaranteed (iterator member type is different for std::vector and std::list, because their implementations are wildly different and they cannot use the same iterator type).
Here's a typical class in C++:
struct Foo
{
int n; // non-static data member
static const float x; // static data member
int f() const; // non-static member function
static int g(); // static member function
template <typename T>
void h(T); // non-static member function template
struct X { bool a; }; // member type
template <typename> struct Q; // member class template
using T = Q<int>; // member type (member typedef)
};
Example usage:
// Use static members
print(Foo::x);
print(Foo::g());
Foo::X y;
Foo::Q<double> z;
Foo::T w;
// Use non-static members (requires instance)
void demo(const Foo & a)
{
print(a.n);
print(a.f());
print(a.h<float>());
}
The page you linked lists a "member type" of "value_type T". This is type definition that belongs to or is a member of T. Consider
using VEC = std::vector<double>;
VEC dv { 4.2 };
Now lets say we want to store a value from dv but we don't want to hard-code the type so that a future change to the definition of VEC will Do The Right Thing (TM).
using VEC = std::vector<double>;
VEC dv { 4.2 };
// ...
VEC::value_type d = dv.front();
Or if you want to write cross-platform portable code, it can be helpful to use 'size_type' to ensure you store sizes that are large enough:
int s = dv.size(); // wrong on 64-bit system
VEC::size_type s = dv.size(); // always correct
That is to say, std::vector<double> hosts a type definition, value_type, which evaluates to the type of 'T' in vector, or in our case double.
This is mostly important for generic programming:
template<typename T>
void dothing(const T& container)
{
T::const_pointer* c = &container; // for some reason I need it's address
// .. other stuff
}
There are two things you need to understand:
You can make aliases for types.
For example,
using blah = int;
would make blah an alias for int. In other words, you would be able to use blah instead of int in any place.
(There is another way to make a type alias using typedef instead of using, but it is less readable.)
You can put those aliases inside of classes.
For example, if you declared a following struct
struct S
{
using type = int;
};
you would be able to use S::type instead of an int.
Such aliases inside of classes are often called member types.

Can't push a map inside a vector?

The following code doesn't work, it gives the following errors:
No matching function for call to object of type 'const comparer'
and
Call to object of type 'value_compare' (aka 'std::_1::_map_value_compare, int, comparer, true>') is ambiguous
Here is the code:
struct comparer
{
bool operator()(const std::string x, const std::string y)
{
return x.compare(y)<0;
}
};
int main(int argc, char **argv)
{
vector< map<string,int,comparer> > valMapVect;
map<string,int,comparer> valMap;
valMapVect.push_back(valMap);
}
It is compiled with Xcode 5.x (so on a Mac).
Somebody has an idea of what is wrong? I think it was working a while ago when I was compiling it on Linux. Is it possible?
It seems libc++ wants the function call operator in comparer to be a const member function:
struct comparer
{
bool operator()(const std::string x, const std::string y) const
{ // ^^^^^ fixes the problem
return x.compare(y)<0;
}
};
Personally I would pass the arguments as std::string const& (note the &) but that doesn't change whether libc++ likes the comparison object. I'm not, yet, sure if the standard mandates that the const is present. I didn't spot such a requirement which would imply that the comparison function would have to be kept as a mutable member. However, given that it is often stateless, it is desirable to derive from it to not waste any memory (i.e., take advantage of the empty base optimization) which is probably what libc++ does. It isn't quite clear whether
it is a bug in libc++, i.e., that the comparison function object has to be stored a mutable member.
in the standard to not mandate the function call operator to be const.
in the code using it to make the function call operator const.
The easiest fix is, however, to make function call operator const.

Calling a static member function of a C++ STL container's value_type

I'm trying to get my head around why the following doesn't work. I have a std::vector and I want to call a static member function of it's contained value_type like so:
std::vector<Vector> v;
unsigned u = v.value_type::Dim();
where Vector is in fact a typedef for a templated type:
template <typename T, unsigned U> class SVector;
typedef SVector<double, 2> Vector; //two-dimensional SVector containing doubles
and the static member function Dim() actually inlines the dimensionality U of the Vector.
Now the compiler returns an error message saying:
error: ‘SVector<double, 2u>’ is not a base of
‘std::vector<SVector<double, 2u>, std::allocator<SVector<double, 2u> > >
which puzzles me. I can replace the apparently offending line by
unsigned u = Vector::Dim();
and that works, but is obviously ugly as it hardcodes assumptions about the value_type of v...
Thanks!
You are accessing the value_type trough the variable instance and not the variable type.
Method 1 - this works:
typedef std::vector<Vector> MyVector;
MyVector v;
unsigned u = MyVector::value_type::Dim();
Method 2 - or this:
std::vector<Vector> v;
unsigned u = std::vector<Vector>::value_type::Dim();
If you typedef like on method 1 you do not hardcode assumptions on vector template parameter and you write clean code.
Edit: Expanded to explain the behavior for this issue as requested by question owner:
The scope resolution operator :: has higher precedence than any other C++ operator. This includes the Member access from an object . operator. Thus when you write something like:
unsigned u= v.value_type::Dim();
this resolves to the following C++ code:
unsigned u = v.SVector<double, 2>::Dim();
and ultimately what is resolved first is the SVector<double, 2>::Dim() part. This would force the vector instance declared trough variable v to have a templatized inner class named SVector. And because this does not happen this results in error:
error C2039: 'SVector<double,2>' : is not a member of 'std::vector<_Ty>'
STL vector would have to be "expanded" for each usage of this pattern (accessing value_type trough variable instance and not variable type). This is not a good solution as it leads to lots of boilerplate and unnecessary and unmaintainable code. By following the above mentioned solutions you avoid all this and can easily do what you wanted.