Propagate constness to data pointed by member variables
As @Alf P. Steinbach noted, you oversaw the fact that copying your pointer would yield a non-const object pointing to the same underlying object.
Pimpl
(below) nicely circumvent the issue by performing a deep-copy,unique_ptr
circumvents it by being non-copyable. It is much easier, of course, if the pointee is owned by a single entity.Boost.Optional propagates const-ness, however it's not exactly a pointer (though it models the OptionalPointee concept). I know of no such other library.
I would favor that they provide it by default. Adding another template parameter (traits class I guess) does not seem worth the trouble. However that would radically change the syntax from a classic pointer, so I am not sure that people would be ready to embrace it.
Code of the Pimpl
class
template <class T>
class Pimpl
{
public:
/**
* Types
*/
typedef T value;
typedef const T const_value;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
/**
* Gang of Four
*/
Pimpl() : _value(new T()) {}
explicit Pimpl(const_reference v) : _value(new T(v)) {}
Pimpl(const Pimpl& rhs) : _value(new T(*(rhs._value))) {}
Pimpl& operator=(const Pimpl& rhs)
{
Pimpl tmp(rhs);
swap(tmp);
return *this;
} // operator=
~Pimpl() { boost::checked_delete(_value); }
void swap(Pimpl& rhs)
{
pointer temp(rhs._value);
rhs._value = _value;
_value = temp;
} // swap
/**
* Data access
*/
pointer get() { return _value; }
const_pointer get() const { return _value; }
reference operator*() { return *_value; }
const_reference operator*() const { return *_value; }
pointer operator->() { return _value; }
const_pointer operator->() const { return _value; }
private:
pointer _value;
}; // class Pimpl<T>
// Swap
template <class T>
void swap(Pimpl<T>& lhs, Pimpl<T>& rhs) { lhs.swap(rhs); }
// Not to be used with pointers or references
template <class T> class Pimpl<T*> {};
template <class T> class Pimpl<T&> {};
How can I propagate const when returning a std::vector<int*> from a const method?
You're asking for std::experimental::propagate_const
. But since it is an experimental feature, there is no guarantee that any specific toolchain is shipped with an implementation. You may consider implementing your own. There is an MIT licensed implementation, however. After including the header:
using namespace xpr=std::experimental;
///...
std::vector<xpr::propagate_const<int*>> my_ptr_vec;
Note however that raw pointer is considered evil so you may need to use std::unique_ptr
or std::shared_ptr
. propagate_const
is supposed to accept smart pointers as well as raw pointer types.
Correct constness with pointer / object / template parameter
There are 2 kinds of constness here. Const data and const handle.
What we want to do is create sanity out of the four combinations:
- const handle, const data = const
- const handle, mutable data = const
- mutable handle, const data = const
- mutable handle, mutable data = mutable
Furthermore, marking a return value as const has no meaning. A return value is an r-value. It will be either copied or moved. This will not result in a const handle at the call site.
So we need to detect constness in 2 places in respect of get_data()
. C++ does the first for us with a const overload. Then we must defer to another template which is evaluated in deduced context so we can use std::enable_if
:
#include <cstddef>
#include <utility>
#include <type_traits>
// default getter - element != const element
template<class Element, typename = void>
struct data_getter
{
using element_type = Element;
using const_element_type = std::add_const_t<element_type>;
// detect mutable container
element_type* operator()(element_type ** pp) const
{
return *pp;
}
// detect const container
const_element_type* operator()(element_type * const * pp) const
{
return *pp;
}
};
// specific specialisation for element == const element
template<class Element>
struct data_getter<Element,
std::enable_if_t<
std::is_same<Element, std::add_const_t<Element>>::value>>
{
// in this case the container's constness is unimportant, so
// we use const because it means only writing one method
Element* operator()(Element *const* p) const
{
return *p;
}
};
template <class T>
class foo
{
public:
using element = T;
using const_element = std::add_const_t<element>;
int rows = 0;
int cols = 0;
element * data = nullptr;
bool reference = false;
public:
foo() = default;
//foo(const foo&) // this is not included here for simplicity
//foo& operator=(const foo&) // this is not included here for simplicity
foo(int r, int c) : rows(r), cols(c)
{
data = new element[rows * cols];
}
~foo()
{
if (!reference)
{
delete[] data;
}
}
decltype(auto) get_data()
{
// defer to getter
return data_getter<element>()(&data);
}
decltype(auto) get_data() const
{
// defer to getter
return data_getter<const_element>()(&data);
}
// this will return a mutable container of const data
foo<const_element> row(int r) const
{
foo<const_element> t;
t.rows = 1;
t.cols = cols;
t.reference = true;
t.data = get_data() + r * cols;
return t;
}
};
int main()
{
foo<int> A(2, 1);
A.get_data()[0] = 1;
auto AC = A.row(0);
auto x = AC.get_data()[0]; // fine
// AC.get_data()[0] = 1; // assignment of read-only location
return 0;
}
Invoking a nonconst method on a member from a const method
When and object of type B
is const, then all of its members are const, which means its two members are, for the duration of B::go()
, effectively
A const a;
A * const aPtr;
The first is a constant object of type A
, on which you can only call const member functions. The second, however, is a constant pointer to a non-constant A
. You could not legally say aPtr = <anything>
from within the function B::go()
, since that would modify aPtr
, which is constant.
A pointer to a constant A
would be declared as A const* aPtr
or const A* aPtr
, which would then make calling the non-constant A::nonconst()
illegal.
C++ - Why don't const functions force const-ness on member-pointers?
EDIT:
Regarding your code: you're never actually changing the member or returning a reference to it.
unsigned int& value2(void) const
{
*m_ptr = 10;
return *m_ptr;
//Why?
}
//returns the value your member points to, not your member
unsigned int* value3(void) const
{
return m_ptr; //Why?
}
//returns a copy of your member
What would be disallowed would be the version I posted in my snippet.
For example:
struct A
{
int * x;
int& getX() const { return *x; }
int* getX1() const { return x; }
int*& getX2() const { return x; } //error here
};
Only the last method will return an error, because:
- first one returns the value, which is not const. The pointer is const.
- second one returns the pointer by value, which again is ok
- the third one actually returns the member by reference, that's why it's illegal
Related Topics
Compile Time String Encryption Using Constexpr
How to Create a Thread-Safe Singleton Pattern in Windows
What Strategies Have You Used to Improve Build Times on Large Projects
How to Perform a Pairwise Binary Operation Between the Elements of Two Containers
Instantiate Class with or Without Parentheses
How to Write an Adl-Enabled Trailing Return Type, or Noexcept Specification
Using Pair as Key in a Map (C++/Stl)
How to Simulate a Key Press in C++
How to Execute a Command and Get Return Code Stdout and Stderr of Command in C++
How to Do Aes Decryption Using Openssl
Real-Time Pitch Detection Using Fft
What Is the Modern, Correct Way to Do Type Punning in C++
C++ - Hold the Console Window Open
Visual Studio 2013 Error Ms8020 Build Tools V140 Cannot Be Found
C++ Forwarding Reference and R-Value Reference