I have a c++ class that has a private array member.
class array_test
{
public:
std::tuple<double*, int> get_vector_member();
private:
double test_array[3];
};
In C++11 is there a better method call to get a double[] that doesn't involve grabbing double as a pointer. I am worried that either myself or someone else will delete the pointer and cause some strange undefined behavior.
I hope the fact I didn't include an implementation for get_vector_member is an issue.
How about providing typical C++ field accessor functions, with a const and non-const overload, returning a reference to the field?
class X
{
double data[3];
public:
// Accessors
double (&get())[3] { return data; }
const double (&get() const)[3] { return data; }
};
In C++14 this code is a bit easier to write:
auto & get() { return data; }
const auto & get() const { return data; }
You could make it an std::array. Some reasons why they're superior to raw arrays:
Easy element access with the front and back member functions.
They're copy constructible and assignable.
Optional bounds-checked access.
Cool comparison operations like ==, != and <.
The fill member function.
No weird syntax when passing by reference.
Then return that by reference:
class array_test
{
public:
const std::array<double, 3>& get_array_member() const;
private:
std::array<double, 3> test_array;
};
Related
I'm creating a type punning class View which takes a pointer to byte and adapts it to an array of T. The issue is that a non-const View can be constructed from a const byte*. I don't want to have separate, incompatible types like a View and ConstView. Maybe I could have a member bool readonly that gets set in the const byte* constructor and is checked for in the non-const operator[] overload, causing it to throw. Is there a better way to handle this?
using std::byte;
template <class T>
class View {
public:
typedef T __attribute__((may_alias)) value_type;
typedef value_type* pointer;
typedef const pointer const_pointer;
typedef value_type& reference;
typedef const reference const_reference;
View(byte* p)
: data { buffer }
{}
View(const byte* p)
: data { const_cast<byte*>(p) }
{}
reference operator[](int index) {
return reinterpret_cast<pointer>(data)[index];
}
const_reference operator[](int index) const {
return reinterpret_cast<const_pointer>(data)[index];
}
private:
byte* data;
};
We can actually do all of this checking at compile-time. You're using std::byte, so I assume you're on at least C++17, which means this is really straightforward (We can do a lot of these tricks with older C++ versions, but it involves more template trickery)
We can use static_assert to enable or disable functions depending on the input type. And we'll use is_const_v to check whether our T type is const or not.
template <class T>
class View {
public:
...
View(std::byte* p)
: data { p } {
static_assert(!std::is_const_v<T>);
}
View(const std::byte* p)
: data { const_cast<std::byte*>(p) } {
static_assert(std::is_const_v<T>);
}
reference operator[](int index) {
static_assert(!std::is_const_v<T>);
return reinterpret_cast<pointer>(data)[index];
}
const_reference operator[](int index) const {
return reinterpret_cast<const_pointer>(data)[index];
}
private:
std::byte* data;
};
static_assert is just like assert, except that it runs when the code is generated rather than when it's run. So we define two constructors. One takes an std::byte* and only exists when T is not constant. The other takes a const std::byte* and only exists when T is constant.
Likewise, we have two overloads for operator[]. The first overload returns a mutable reference but can only be used if T is non-const. The second returns a const reference can be used in general. We don't need any assertions for it. (The C++ standard library uses that idiom all over the place: One function returns a constant reference from a const this pointer and one returns a mutable reference, and C++'s overloading rules can handle it)
To use
View<int> x { new std::byte[1] };
View<const int> y { const_cast<const std::byte*>(new std::byte[1]) };
// All fine
x[0] = 100;
std::cout << x[0] << std::endl;
std::cout << y[0] << std::endl;
// Fails at compile time
// y[0] = 100;
return 0;
Also, you'll want to give Rule of Three/Five a thorough read at some point soon. You're taking a pointer as argument, so you need to understand how to manage that resource. You'll either need to (preferred) take a smart pointer rather than a raw one, or if you insist on the raw pointer then you need to write your own or delete the destructor, move and copy constructors, and move and copy assignment operators.
class Element {
class Point {
private:
double x;
double y;
public:
//getters/setters for x and y
};
private:
std::string name;
std::vector<Point> values;
public:
void insertValue(unsigned int index, double x, double y);
...
};
class Collection {
private:
std::string name;
std::vector<Element> elements;
public:
...
...
Element* elementAt(unsigned int index) const {
return const_cast<Element*>(&elements.at(index));
}
};
What I need is to get an Element in a certain index from the collection to do operations like Element::insertValues. It is a bad practice doing it as it's done in the Collection::elementAt method (using const_cast)?
Is it better to remove the const qualifier from the method? I marked the method const since the method itself does not modify the object.
The usual idiom for this is to have two methods, a const one and a non-const one. In this case one returns a const Element *, and the other one returns an Element *, keeping everything const-correct.
const Element* elementAt(unsigned int index) const {
return &elements.at(index);
}
Element* elementAt(unsigned int index) {
return &elements.at(index);
}
Yes, it is true that this leads to some code duplication. Such is life, noone has ever accused C++ of being compact and concise.
P.S.: you didn't ask this, but an even better idiom would be to return a reference, rather than a pointer. std::vector::at returns a reference, why shouldn't your pinch-hitter do the same?
Let
class A
{
std::vector<std::shared_ptr<int>> v_;
};
Now I'd like to add access to v_ using two public member functions
std::vector<std::shared_ptr<int>> const & v() { return v_; }
and
std::vector<std::shared_ptr<int const> const & v() const { TODO }
I cannot replace TODO with return v_; though.
One option would be to not return a reference but a copy. Apart from the obvious performance penalty, this would also make the interface somewhat less desirable.
Another option is to make TODO equal to return reinterpret_cast<std::vector<std::shared_ptr<int const>> const &>(v_);
My question is, is this undefined behavior? Or, alternatively, is there a better option, preferably without using reinterpret_cast?
A way to avoid copying the container is to provide transform iterators that transform the element on dereference:
#include <vector>
#include <memory>
#include <boost/iterator/transform_iterator.hpp>
class A
{
std::vector<std::shared_ptr<int> > v_;
struct Transform
{
template<class T>
std::shared_ptr<T const> operator()(std::shared_ptr<T> const& p) const {
return p;
}
};
public:
A() : v_{std::make_shared<int>(1), std::make_shared<int>(2)} {}
using Iterator = boost::transform_iterator<Transform, std::vector<std::shared_ptr<int> >::const_iterator>;
Iterator begin() const { return Iterator{v_.begin()}; }
Iterator end() const { return Iterator{v_.end()}; }
};
int main() {
A a;
// Range access.
for(auto const& x : a)
std::cout << *x << '\n';
// Indexed access.
auto iterator_to_second_element = a.begin() + 1;
std::cout << **iterator_to_second_element << '\n';
}
Putting aside the discussion of whether or not you should return a reference to a member...
std::vector already propagates its own const qualifier to the references, pointee's and iterators it returns. The only hurdle is making it propagate further to the pointee type of the std::shared_ptr. You can use a class like std::experimental::propagate_const (that will hopefully be standardized) to facilitate that. It will do as its name implies, for any pointer or pointer-like object it wraps.
class A
{
using ptr_type = std::experimental::propagate_const<std::shared_ptr<int>>;
std::vector<ptr_type> v_;
};
Thus TODO can become return v_;, and any access to the pointees (like in the range-based for you wish to support) will preserve const-ness.
Only caveat is that it's a moveable only type, so copying out an element of the vector will require a bit more work (for instance, by calling std::experimental::get_underlying) with the element type of the vector itself.
This question already has answers here:
Is casting std::pair<T1, T2> const& to std::pair<T1 const, T2> const& safe?
(3 answers)
Closed 8 years ago.
This code demonstrates the problem I'm trying to solve:
#include <map>
class Point
{
public:
float m_x;
float m_y;
};
typedef std::set<Point *> PointSet;
typedef std::set<const Point * const> ConstPointSet;
float GetMinimumRange(const ConstPointSet &pointSet)
{
float minimumRange(0.0f);
// find the smallest distance between any pair of points in the set
return minimumRange;
}
float GetMinimumRangeWrong(const PointSet &pointSet)
{
PointSet::iterator first(pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
class PointSet_
{
public:
std::set<Point *> m_pointSet;
float GetMinumumRange() const
{
PointSet::iterator first(m_pointSet.begin());
Point * point(*first);
point->m_x = 42.0f; // I want to prevent this
return 0.0f;
}
};
void test()
{
PointSet myPointSet;
// Add some points to my set
// This fails because the compiler states it can't convert from PointSet to ConstPointSet.
//float minimumRange1(GetMinimumRange(myPointSet));
// reinterpret_cast<> is the only cast that works here, const_cast fails with the same
// complaint as the line above generates
ConstPointSet *myConstPointSet(reinterpret_cast<ConstPointSet *>(&myPointSet));
float minimumRange1(GetMinimumRange(*myConstPointSet));
float minimumRange2(GetMinimumRangeWrong(myPointSet));
}
I want to create a routine that takes a PointSet, evaluates the minimum range between any pair of Points in the set, but that it guarantees that it won't modify the PointSet passed to it in any way at all. It can't modify the members of any referenced Point, it can't change the pointers themselves, nor can it add or remove members from the set
The issue is that the compiler correctly views PointSet and ConstPointSet as different types because of the difference of const qualifiers of the inner type, and therefore refuses to cast between them, even though I'm only adding const qualifiers.
I tried creating a class to contain a PointSet, and creating a const member function, but even in there it allows modification to one of the inner Points. At least MSVC will compile that without complaint. I'll confess I was quite surprised about this.
The only way I've found that works is to use a reinterpret_cast<> to convert a pointer to a PointSet to a pointer to a ConstPointSet. The standard does note that reinterpret_cast<> can be used to add const qualifiers, but does that apply in this case?
If not, is there any way to do what I want? I realize that good code discipline can be used to ensure that GetMinimumRange() doesn't modify the passed PointSet, but I'd like to get those const qualifiers in there for two reasons.
They will ensure that if anyone ever modifies GetMinimumRange() they can't cause it to modify the PointSet.
It will allow the compiler to optimize over the call to GetMinimumRange(). In the absence of the const qualifiers, no assumptions can be made at the calling site regarding values that could be cached across the call, thus possibly leading to redundant fetches of data.
There is no straightforward way, because constness does not propagate through pointers. In a const PointSet, it's the pointers themselves that are const, not the objects they point to. And, like you've discovered, const Point * is a different type from Point *, so std::set<const Point *> is a different type from std::set<Point *>.
I don't like the reinterpret_cast of a STL structure. That is scary to me. STL does all kinds of optimizations based on the type of template parameters. std::vector<bool> being an extreme example. You'd think that std::set<T *> and std::set<const T *> would be laid out the same because they are both pointers, but I wouldn't assume so until I read it in the Standard.
If it were a structure I had written myself, and I could easily verify that the cast would work, it would be less scary but still ugly.
You could write a wrapper class that holds a reference to a std::set<Point *> but only allows const access to its pointed-to Points via iterators. If the pointers are guaranteed to be non-null, your iterator can dereference the points directly. I've written it here as a template:
template <typename T>
class PointerSetViewer
{
public:
PointerSetViewer(std::set<T *> const &set) : set(set) {}
struct iterator : public std::iterator<std::forward_iterator_tag, T const>
{
iterator(typename std::set<T *>::const_iterator it) : it(it) {}
T const &operator*() const { return **it; }
T const *operator->() const { return *it; }
iterator &operator++() { ++it; return *this; }
bool operator==(iterator other) { return it == other.it; }
bool operator!=(iterator other) { return it != other.it; }
private:
typename std::set<T *>::const_iterator it;
};
iterator begin() { return iterator(set.cbegin()); }
iterator end() { return iterator(set.cend()); }
private:
std::set<T *> const &set;
};
It's bulky, but it accomplishes your goals without doing anything risky:
float GetMinimumRangeWrong(PointerSetViewer<Point> &pointSet)
{
PointerSetViewer<Point>::iterator first(pointSet.begin());
first->m_x = 42.0f; // does not compile
}
Also if you're using C++11, you can get some nice range-based for loops:
template <typename T>
PointerSetViewer<T> view_set(std::set<T *> const &set) {
return PointerSetViewer<T>(set);
}
for (Point const &p : view_set(myPointSet)) {
// whatever...
}
Baroque? Yes, but if one piece of baroque library code lets you write 100 pieces of beautiful application code with better type checking, it's probably worth it.
Edit: this doesn't work for set. As pointed out in comments, a non-const set is defined to hold const T, so there is actually nothing we can do.
At this stage I don't see a viable solution other than making PointSet_ actually wrap the set properly, i.e. have the set be private and be careful in your public functions.
Here is a solution I came up with; make the set contain a little wrapper which will propagate the const-ness of itself onto the pointer.
I would have thought there would be a pre-existing class that does this, but none of the std smart pointer classes seem to.
#include <iostream>
#include <set>
template<typename T>
struct qualifier_ptr
{
T *operator->() { return ptr; }
T const *operator->() const { return ptr; }
operator T*() { return ptr; }
operator T const*() const { return ptr; }
qualifier_ptr(T *p): ptr(p) {}
private:
T *ptr;
};
struct Point
{
float m_x;
float m_y;
};
struct PointSet
{
typedef std::set< qualifier_ptr<Point> > SetType;
SetType points;
float foo() const
{
//Point *p = *points.begin(); // error
Point const *p = *points.begin(); // OK
return 0;
}
};
int main()
{
PointSet ps;
PointSet const &cps = ps;
ps.foo(); // OK
cps.foo(); // OK
}
I normally don't like to use conversion operators but it seems appropriate here.
As you stated in the comments that the set is built only once per session, I'd suggest just creating the ConstPointerSet by making a copy:
void test()
{
PointSet myPointSet;
// Add some points to my set
ConstPointSet myConstPointSet{ begin(myPointSet), end(myPointSet) };
float minimumRange1(GetMinimumRange(myConstPointSet));
}
Or wrapp it into a function:
ConstPointSet toConst(const PointSet& pSet){
return ConstPointSet{ cbegin(pSet), cend(pSet) };
}
If you don't need the semantics of a set I'd recommend using a std::vector instead, which is much more efficient to copy or traverse.
I have a class which has a member of type std:vector
private:
std::vector<int> myVector;
I have created Get method to access myVector
1. const std::vector<int> GetMyVector() const;
2. const void GetMyVector(std::vector<int>& vec) const;
The implementations are as follows respectively:
1. const std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
2. const void MyClass::GetMyVector(std::vector<int>& vec) const
{
vec = myVector;
}
Which one of the two Get methods is better and why?
I'd prefer option 3:
const std::vector<int>& MyClass::GetMyVector() const
{
return myVector;
}
Your option 1 returned a copy of myVector. This returns a const (so read-only) reference to the class member.
Why return the vector at all?
int MyClass::GetItem(const size_t index) const
{
return myVector[index];
}
First of all you are exposing your implementation when you return a private member of a class from a member function, which usually is bad design. Take a look at #JoachimPileborg's solution for an example of how to avoid this.
If you want to return a copy then you should return by value.
If you want to return a reference to an object then return by reference. However, bear in mind that when the object is destructed you will end up with a dangling reference, e.g.
class Foo {
public:
std::vector<int>& getVec() {
return myVec;
}
private:
std::vector<int> myVec;
};
int main() {
Foo* f = new Foo();
std::vector<int>& myRef = f->getVec();
delete f;
std::cout << myRef.size(); // The demons come! Dangling reference!
}
Because of this, it is often the right thing to return a copy instead of a reference.
If you are returning an object by copy then there is no any sense to declare it as const. So instead of
const std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
I would write
std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
The second declaration is worst than the first one because it only confuses users. It is not clear whether the corresponding data member of the class is assigned to the parameter or this method makes some changes of the parameter without assigning the corresponding data member to the parameter.
So considering suggested variants by you I would choise declaration
std::vector<int> MyClass::GetMyVector() const
{
return myVector;
}
As a rule of thumb always try to return const reference of a class member .
So use const std::vector<int> & MyClass::GetMyVector() const