Related
I'm working on learning C++ with Stroustrup's (Programming Principles & Practice Using C++) book. In an exercise we define a simple struct:
template<typename T>
struct S {
explicit S(T v):val{v} { };
T& get();
const T& get() const;
void set(T v);
void read_val(T& v);
T& operator=(const T& t); // deep copy assignment
private:
T val;
};
We're then asked to define a const and a non-const member function to get val.
I was wondering: Is there any case where it makes sense to have non-const get function that returns val?
It seems much cleaner to me that we can't change the value in such situations indirectly. What might be use cases where you need a const and a non-const get function to return a member variable?
Non-const getters?
Getters and setters are merely convention. Instead of providing a getter and a setter, a sometimes used idiom is to provide something along the line of
struct foo {
int val() const { return val_; }
int& val() { return val_; }
private:
int val_;
};
Such that, depending on the constness of the instance you get a reference or a copy:
void bar(const foo& a, foo& b) {
auto x = a.val(); // calls the const method returning an int
b.val() = x; // calls the non-const method returning an int&
};
Whether this is good style in general is a matter of opinion. There are cases where it causes confusion and other cases where this behaviour is just what you would expect (see below).
In any case, it is more important to design the interface of a class according to what the class is supposed to do and how you want to use it rather than blindly following conventions about setters and getters (eg you should give the method a meaningful name that expresses what it does, not just in terms of "pretend to be encapsulated and now provide me access to all your internals via getters", which is what using getters everywhere actually means).
Concrete example
Consider that element access in containers is usually implemented like this. As a toy example:
struct my_array {
int operator[](unsigned i) const { return data[i]; }
int& operator[](unsigned i) { return data[i]; }
private:
int data[10];
};
It is not the containers job to hide the elements from the user (even data could be public). You dont want different methods to access elements depending on whether you want to read or write the element, hence providing a const and a non-const overload makes perfectly sense in this case.
non-const reference from get vs encapsulation
Maybe not that obvious, but it is a bit controversial whether providing getters and setters supports encapsulation or the opposite. While in general this matter is to a large extend opinion based, for getters that return non const references it is not so much about opinions. They do break encapuslation. Consider
struct broken {
void set(int x) {
counter++;
val = x;
}
int& get() { return x; }
int get() const { return x; }
private:
int counter = 0;
int value = 0;
};
This class is broken as the name suggests. Clients can simply grab a reference and the class has no chance to count the number of times the value is modified (as the set suggests). Once you return a non-const reference then regarding encapsulation there is little difference to making the member public. Hence, this is used only for cases where such behaviour is natural (eg container).
PS
Note that your example returns a const T& rather than a value. This is reasonable for template code, where you dont know how expensive a copy is, while for an int you wont gain much by returning a const int& instead of an int. For the sake of clarity I used non-template examples, though for templated code you would probably rather return a const T&.
First let me rephrase your question:
Why have a non-const getter for a member, rather than just making the member public?
Several possible reasons reasons:
1. Easy to instrument
Whoever said the non-const getter needs to be just:
T& get() { return val; }
? it could well be something like:
T& get() {
if (check_for_something_bad()) {
throw std::runtime_error{
"Attempt to mutate val when bad things have happened");
}
return val;
}
However, as #BenVoigt suggests, it is more appropriate to wait until the caller actually tries to mutate the value through the reference before spewing an error.
2. Cultural convention / "the boss said so"
Some organizations enforce coding standards. These coding standards are sometimes authored by people who are possibly overly-defensive. So, you might see something like:
Unless your class is a "plain old data" type, no data members may be public. You may use getter methods for such non-public members as necessary.
and then, even if it makes sense for a specific class to just allow non-const access, it won't happen.
3. Maybe val just isn't there?
You've given an example in which val actually exists in an instance of the class. But actually - it doesn't have to! The get() method could return some sort of a proxy object, which, upon assignment, mutation etc. performs some computation (e.g. storing or retrieving data in a database; or flipping a bit, which itself is not addressable like an object needs to be).
4. Allows changing class internals later without changing user code
Now, reading items 1. or 3, above, you might ask "but my struct S does have val!" or "by my get() doesn't do anything interesting!" - well, true, they don't; but you might want to change this behavior in the future. Without a get(), all of your class' users will need to change their code. With a get(), you only need to make changes to the implementation of struct S.
Now, I don't advocate for this kind of a design approach approach, but some programmers do.
get() is callable by non const objects which are allowed to mutate, you can do:
S r(0);
r.get() = 1;
but if you make r const as const S r(0), the line r.get() = 1 no longer compile, not even to retrieve the value, that's why you need a const version const T& get() const to at least to able to retrieve the value for const objects, doing so allows you do:
const S r(0)
int val = r.get()
The const version of member functions try to be consistent with the constness property of the object the call is made on, i.e if the object is immutable by being const and the member function returns a reference, it may reflect the constness of the caller by returning a const reference, thus preserving the immutability property of the object.
It depends on the purpose of S. If it's some kind of a thin wrapper, it might be appropriate to allow the user to access the underlaying value directly.
One of the real-life examples is std::reference_wrapper.
No. If a getter simply returns a non-const reference to a member, like this:
private:
Object m_member;
public:
Object &getMember() {
return m_member;
}
Then m_member should be public instead, and the accessor is not needed. There is absolutely no point making this member private, and then create an accessor, which gives all access to it.
If you call getMember(), you can store the resulting reference to a pointer/reference, and afterwards, you can do whatever you want with m_member, the enclosing class will know nothing about it. It's the same, as if m_member had been public.
Note, that if getMember() does some additional task (for example, it doesn't just simply return m_member, but lazily constructs it), then getMember() could be useful:
Object &getMember() {
if (!m_member) m_member = new Object;
return *m_member;
}
When I am designing a generic class, I am often in dilemma between the following design choices:
template<class T>
class ClassWithSetter {
public:
T x() const; // getter/accessor for x
void set_x(const T& x);
...
};
// vs
template<class T>
class ClassWithProxy {
struct Proxy {
Proxy(ClassWithProxy& c /*, (more args) */);
Proxy& operator=(const T& x); // allow conversion from T
operator T() const; // allow conversion to T
// we disallow taking the address of the reference/proxy (see reasons below)
T* operator&() = delete;
T* operator&() const = delete;
// more operators to delegate to T?
private:
ClassWithProxy& c_;
};
public:
T x() const; // getter
Proxy x(); // this is a generalization of: T& x();
// no setter, since x() returns a reference through which x can be changed
...
};
Notes:
the reason why I return T instead of const T& in x() and operator T() is because a reference to x might not be available from within the class if x is stored only implicitly (e.g. suppose T = std::set<int> but x_ of type T is stored as std::vector<int>)
suppose caching of Proxy objects and/or x is not allowed
I am wondering what would be some scenarios in which one would prefer one approach versus the other, esp. in terms of:
extensibility / generality
efficiency
developer's effort
user's effort
?
You can assume that the compiler is smart enough to apply NRVO and fully inlines all the methods.
Current personal observations:
(This part is not relevant for answering the question; it just serves as a motivation and illustrates that sometimes one approach is better than the other.)
One particular scenario in which the setter approach is problematic is as follows. Suppose you're implementing a container class with the following semantics:
MyContainer<T>& (mutable, read-write) - allows modifying on both the container and its data
implementation of the
MyContainer<const T>& (mutable, read-only) - allows modifying to the container but not its data
const MyContainer<T> (immutable, read-write) - allows modifying the data but not the container
const MyContainer<const T> (immutable, read-only) - no modifying to the container/data
where by "container modifications" I mean operations like adding/removing elements. If I implement this naively with a setter approach:
template<class T>
class MyContainer {
public:
void set(const T& value, size_t index) const { // allow on const MyContainer&
v_[index] = value; // ooops,
// what if the container is read-only (i.e., MyContainer<const T>)?
}
void add(const T& value); // disallow on const MyContainer&
...
private:
mutable std::vector<T> v_;
};
The problem could be mitigated by introducing a lot of boilerplate code that relies on SFINAE (e.g. by deriving from a specialized template helper which implements both versions of set()). However, a bigger problem is that this brakes the common interface, as we need to either:
ensure that calling set() on an read-only container is a compile error
provide a different semantics for the set() method for read-only containers
On the other hand, while the Proxy-based approach works neatly:
template<class T>
class MyContainer {
typedef T& Proxy;
public:
Proxy get(const T& value, size_t index) const { // allow on const MyContainer&
return v_[index]; // here we don't even need a const_cast, thanks to overloading
}
...
};
and the common interface and semantics is not broken.
One difficulty I see with the proxy approach is supporting the Proxy::operator&()
because there might be no object of type T stored / a reference to available (see notes above). For example, consider:
T* ptr = &x();
which cannot be supported unless x_ is actually stored somewhere (either in the class itself or accessible through a (chain of) methods called on member variables), e.g.:
template<class T>
T& ClassWithProxy::Proxy::operator&() {
return &c_.get_ref_to_x();
}
Does that mean that the proxy object references are actually superior when T& is available (i.e. x_ is explicitly stored) as it allows for:
batching/delaying updates (e.g. imagine the changes are propagated from the proxy class destructor)
better control over caching
?
(In that case, the dilemma is between void set_x(const T& value) and T& x().)
Edit: I changed the typos in constness of setters/accessors
Like most design dilemmas, I think this depends on the situation. Overall, I would prefer the getters and setters pattern, as it is simpler to code (No need for a proxy class for every field), simpler to understand by another person (looking at your code), and more explicit in certain circumstances. However, there are situations where proxy classes can simplify user experience and hide implementation details. A few examples:
If your container is some sort of associative array, you might overload operator[] for getting and setting the value for a particular key. However, if a key hasn't been defined, you might need a special operation for adding it. Here a proxy class would probably be the most convenient solution, as it can handle = assignment in different ways as necessary. However, this can mislead users: If this particular data structure has different times for adding vs setting, using a proxy makes this difficult to see, while using a set and put method set can make it clear the separate time used by each operation.
What if the container does some sort of compression on T and stores the compressed form? While you could use a proxy which did the compression/decompression whenever necessary, it would hide the cost associated with de/re compression from the user, and they might use it as if it were a simple assignment without heavy computation. By creating getter/setter methods with appropriate names, it can be made more apparent that they take significant computational effort.
Getters and setters also seem more extensible. Making a getter and setter for a new field is easy, while making a proxy which forwards the operations for every property would be an error-prone annoyance. What if you later need to extend your container class? With getters and setters, just make them virtual and override them in the subclass. For proxies, you might have to make a new proxy struct in each subclass. To avoid breaking encapsulation you probably should make your proxy struct use the superclasses's proxy struct to do some of the work, which could get quite confusing. With getters/setters, just call the super getter/setter.
Overall, getters and setters are easier to program, understand and change, and they can make visible the costs associated with an operation. So, in most situations, I would prefer them.
I think your ClassWithProxy interface is mixing wrappers/proxys and containers. For containers it is common to use accessors like
T& x();
const T& x() const;
just like the standard containers do, e.g. std::vector::at(). But normally access to members by reference breaks encapsulation. For containers it's a convinience and part of the design.
But you noted that a reference to T is not always available, so this will reduce the options to your ClassWithSetter interface, which should be a wrapper for T dealing with the way you store your type (while containers are dealing with the way you store objects). I would change the naming, to make clear, it might not be as efficient as a plain get/set.
T load() const;
void save(const T&);
or something more in context. Now it should be obvious, modifying T by using a proxy, again breaks encapsulation.
By the way, there is no reason not to use the wrapper inside of a container.
I think that possibly part of the problem with your set implementation is that your idea of how a const MyContainer<T>& would behave is inconsistent with how standard containers behave and therefore would likely confuse future code maintainers. The normal container type for "constant container, mutable elements" is const MyContainer<T*>& where you add a level of indirection to clearly indicate your intention to users.
This is how the standard containers work, and if you utilize that mechanism you don't need the underlying container to be mutable nor the set function to be const.
All that said I slightly prefer the set/get approach because if a particular attribute only needs a get you don't have to write a set at all.
However I prefer not writing any direct access to members (like get/set or proxy) but instead providing a meaningfully named interface through which clients can access the class functionality. In a trivial example to show my meaning, instead of set_foo(1); set_bar(2); generate_report(); prefer a direct interface like generate_report(1, 2); and avoid directly manipulating class attributes.
I dug up an old Grid class, which is just a simple 2-D container templated with a type. To make one you would do this:
Grid<SomeType> myGrid (QSize (width, height));
I tried to make it "Qt-ish"...for instance it does size operations in terms of QSize, and you index into it with myGrid[QPoint (x, y)]. It can take boolean masks and do operations on elements whose mask bit was set. There's also a specialization where if your elements are QColor it can generate a QImage for you.
But one major Qt idiom I adopted was that it did implicit sharing under the hood. This turned out to be very useful in the QColor-based grids for the Thinker-Qt-based program I had.
However :-/ I also happened to have some cases where I'd written the likes of:
Grid< auto_ptr<SomeType> > myAutoPtrGrid (QSize (width, height));
When I moved up from auto_ptr to C++11's unique_ptr, the compiler rightfully complained. Implicit sharing requires the ability to make an identical copy if needed...and auto_ptr had swept this bug under the rug by conflating copying with transfer-of-ownership. Non-copyable types and implicit sharing simply do not mix, and unique_ptr is kind enough to tell us.
(Note: It so happened that I hadn't noticed the problem in practice, because the use cases for the auto_ptr were passing grids by reference...never by value. Still, this was bad code...and the proactive nature of C++11 is pointing out the potential problem before it happens.)
Ok, so...how might I design a generic container that can flip implicit sharing on and off? I really did want many of the Grid features when I was using the auto_ptr and it's great if copying is disabled for non-copyable types...that catches errors! But having the implicit sharing work is nice as a default, when the type happens to be copyable.
Some ideas:
I could make separate types (NonCopyableGrid, CopyableGrid)...or (UniqueGrid, Grid) depending on your tastes...
I could pass a flag into the Grid constructor
I could use static factory methods (Grid::newNonCopyable, Grid::newCopyable) but which would call the relevant constructor under the hood...maybe more descriptive
If possible, I might "detect" copyability on the contained type, and then either leverage a QSharedDataPointer in the implementation or not, depending?
Any good reasons to pick one of these methods over the others, or have people adopted something altogether better for this kind of situation?
If you were going to do it in a single container, I think the easiest way would be to use std::is_copy_constructable to choose whether your data struct inherited from QSharedData, and to replace QSharedDataPointer with std::unique_ptr (QScopedPointer doesn't support move semantics)
This is only a rough example of what I'm thinking as I don't have Qt and C++11 available together:
template<class T>
class Grid
{
struct EmptyStruct
{
};
typedef typename std::conditional<
std::is_copy_constructible<T>::value,
QSharedData,
EmptyStruct
>::type GridDataBase;
struct GridData : public GridDataBase
{
// data goes here
};
typedef typename std::conditional<
std::is_copy_constructible<T>::value,
QSharedDataPointer<GridData>,
std::unique_ptr<GridData>
>::type GridDataPointer;
public:
Grid() : data_(new GridData) {}
private:
GridDataPointer data_;
};
Disclaimer
I don't really understand your Grid template or your use cases. However I do understand containers in general. So maybe this answer applies to your Grid<T> and maybe it doesn't.
Since you've already stated the intent that Grid< unique_ptr<T> > would indicate unique ownership and a non-copyable T, what about doing something similar with copy on write?
What about explicitly stating when you want to use copy on write with:
Grid< cow_ptr<T> >
A cow_ptr<T> would offer reference counting copies, but on a "non-const dereference" would do a copy of T if the refcount is not 1. So Grid need not worry about memory management to such an extent. It would need only to handle its data buffer, and perhaps move or copy its members around in Grid's copy and/or move members.
A cow_ptr<T> is fairly easily cobbled together by wrapping std::shared_ptr<T>. Here is a partial implementation I put together about a month ago when dealing with a similar issue:
template <class T>
class cow_ptr
{
std::shared_ptr<T> ptr_;
public:
template <class ...Args,
class = typename std::enable_if
<
std::is_constructible<std::shared_ptr<T>, Args...>::value
>::type
>
explicit cow_ptr(Args&& ...args)
: ptr_(std::forward<Args>(args)...)
{}
explicit operator bool() const noexcept {return ptr_ != nullptr;}
T const* read() const noexcept {return ptr_.get();}
T * write()
{
if (ptr_.use_count() > 1)
ptr_.reset(ptr_->clone());
return ptr_.get();
}
T const& operator*() const noexcept {return *read();}
T const* operator->() const noexcept {return read();}
void reset() {ptr_.reset();}
template <class Y>
void
reset(Y* p)
{
ptr_.reset(p);
}
};
I chose to make the "write" syntax very explicit, since COW tends to be more effective when there are very few writes, but many reads/copies. To gain const access, you use it just like any other pointer:
p->inspect(); // compile time error if inspect() isn't const
But to do some modifying operation you have to call it out with the write member function:
p.write()->modify();
shared_ptr has a bunch of really handy constructors and I didn't want to have to replicate all of them in cow_ptr. So the one cow_ptr constructor you see is a poor man's implementation of inheriting constructors that also works for data members.
You may need to fill this out with other smart pointer functionality such as relational operators. You may also want to change how cow_ptr copies a T. I'm currently assuming a virtual clone() function but you could easily substitute into write the use of T's copy constructor instead.
If an explicit Grid< cow_ptr<T> > doesn't really fit your needs, that's all good. I figured I'd share just in case it did.
I often find myself having to define two versions of a function in order to have one that is const and one which is non-const (often a getter, but not always). The two vary only by the fact that the input and output of one is const, while the input and output of the other is non-const. The guts of the function - the real work, is IDENTICAL.
Yet, for const-correctness, I need them both. As a simple practical example, take the following:
inline const ITEMIDLIST * GetNextItem(const ITEMIDLIST * pidl)
{
return pidl ? reinterpret_cast<const ITEMIDLIST *>(reinterpret_cast<const BYTE *>(pidl) + pidl->mkid.cb) : NULL;
}
inline ITEMIDLIST * GetNextItem(ITEMIDLIST * pidl)
{
return pidl ? reinterpret_cast<ITEMIDLIST *>(reinterpret_cast<BYTE *>(pidl) + pidl->mkid.cb) : NULL;
}
As you can see, they do the same thing. I can choose to define one in terms of the other using yet more casts, which is more appropriate if the guts - the actual work, is less trivial:
inline const ITEMIDLIST * GetNextItem(const ITEMIDLIST * pidl)
{
return pidl ? reinterpret_cast<const ITEMIDLIST *>(reinterpret_cast<const BYTE *>(pidl) + pidl->mkid.cb) : NULL;
}
inline ITEMIDLIST * GetNextItem(ITEMIDLIST * pidl)
{
return const_cast<ITEMIDLIST *>(GetNextItem(const_cast<const ITEMIDLIST *>(pidl));
}
So, I find this terribly tedious and redundant. But if I wish to write const-correct code, then I either have to supply both of the above, or I have to litter my "consumer-code" with const-casts to get around the problems of having only defined one or the other.
Is there a better pattern for this? What is the "best" approach to this issue in your opinion:
providing two copies of a given function - the const and non-const versions
or just one version, and then requiring consumers of that code to do their casts as they will?
Or is there a better approach to the issue entirely?
Is there work being done on the language itself to mitigate or obviate this issue entirely?
And for bonus points:
do you find this to be an unfortunate by-product of the C++ const-system
or do you find this to be tantamount to touching the very heights of mount Olympus?
EDIT:
If I supply only the first - takes const returns const, then any consumer that needs to modify the returned item, or hand the returned item to another function that will modify it, must cast off the constness.
Similarly, if I supply only the second definition - takes non-const and returns non-const, then a consumer that has a const pidl must cast off the constness in order to use the above function, which honestly, doesn't modify the constness of the item itself.
Maybe more abstraction is desirable:
THING & Foo(THING & it);
const THING & Foo(const THING & it);
I would love to have a construct:
const_neutral THING & Foo(const_neutral THING & it);
I certainly could do something like:
THING & Foo(const THING & it);
But that's always rubbed me the wrong way. I am saying "I don't modify the contents of your THING, but I'm going to get rid of the constness that you entrusted me with silently for you in your code."
Now, a client, which has:
const THING & it = GetAConstThing();
...
ModifyAThing(Foo(it));
That's just wrong. GetAConstThing's contract with the caller is to give it a const reference. The caller is expected NOT TO MODIFY the thing - only use const-operations on it. Yes, the caller can be evil and wrong and cast away that constness of it, but that's just Evil(tm).
The crux of the matter, to me, is that Foo is const-neutral. It doesn't actually modify the thing its given, but its output needs to propagate the constness of its argument.
NOTE: edited a 2nd time for formatting.
IMO this is an unfortunate by-product of the const system, but it doesn't come up that often: only when functions or methods give out pointers/references to something (whether or not they modify something, a function can't hand out rights that it doesn't have or const-correctness would seriously break, so these overloads are unavoidable).
Normally, if these functions are just one short line, I'd just reduplicate them. If the implementation is more complicated, I've used templates to avoid code reduplication:
namespace
{
//here T is intended to be either [int] or [const int]
//basically you can also assert at compile-time
//whether the type is what it is supposed to be
template <class T>
T* do_foo(T* p)
{
return p; //suppose this is something more complicated than that
}
}
int* foo(int* p)
{
return do_foo(p);
}
const int* foo(const int* p)
{
return do_foo(p);
}
int main()
{
int* p = 0;
const int* q = foo(p); //non-const version
foo(q); //const version
}
The real problem here appears to be that you're providing the outside world with (relatively) direct access to the internals of your class. In a few cases (e.g., container classes) that can make sense, but in most it means you're providing low-level access to the internals as dumb data, where you should be looking at the higher-level operations that client code does with that data, and then provide those higher-level operations directly from your class.
Edit: While it's true that in this case, there's apparently no class involved, the basic idea remains the same. I don't think it's shirking the issue either -- I'm simply pointing out that while I agree that it is an issue, it's only that arises only rather infrequently.
I'm not sure low-level code justifies such things either. Most of my code is much lower level than most people ever have much reason to work with, and I still only encounter it rather infrequently.
Edit2: I should also mention that C++ 0x has a new definition of the auto keyword, along with a new keyword (decltype) that make a fair number of things like this considerably easier to handle. I haven't tried to implement this exact function with them, but this general kind of situation is the sort of thing for which they're intended (e.g., automatically figuring out a return type based on passed arguments). That said, they normally do just a bit more than you want, so they might be a bit clumsy (if useful at all) for this exact situation.
I don't believe it's the deficiency of const-correctness per se, but rather the lack of convenient ability to generalize a method over cv-qualifiers (in the same way we can generalize over types via templates). Hypothetically, imagine if you could write something like:
template<cvqual CV>
inline CV ITEMIDLIST* GetNextItem(CV ITEMIDLIST * pidl)
{
return pidl ? reinterpret_cast<CV ITEMIDLIST *>(reinterpret_cast<CV BYTE *>(pidl) + pidl->mkid.cb) : NULL;
}
ITEMIDLIST o;
const ITEMIDLIST co;
ITEMIDLIST* po = GetNextItem(&o); // CV is deduced to be nothing
ITEMIDLIST* pco = GetNextItem(&co); // CV is deduced to be "const"
Now you can actually do this kind of thing with template metaprogramming, but this gets
messy real quick:
template<class T, class TProto>
struct make_same_cv_as {
typedef T result;
};
template<class T, class TProto>
struct make_same_cv_as<T, const TProto> {
typedef const T result;
};
template<class T, class TProto>
struct make_same_cv_as<T, volatile TProto> {
typedef volatile T result;
};
template<class T, class TProto>
struct make_same_cv_as<T, const volatile TProto> {
typedef const volatile T result;
};
template<class CV_ITEMIDLIST>
inline CV_ITEMIDLIST* GetNextItem(CV_ITEMIDLIST* pidl)
{
return pidl ? reinterpret_cast<CV_ITEMIDLIST*>(reinterpret_cast<typename make_same_cv_as<BYTE, CV_ITEMIDLIST>::result*>(pidl) + pidl->mkid.cb) : NULL;
}
The problem with the above is the usual problem with all templates - it'll let you pass object of any random type so long as it has the members with proper names, not just ITEMIDLIST. You can use various "static assert" implementations, of course, but that's also a hack in and of itself.
Alternatively, you can use the templated version to reuse the code inside your .cpp file, and then wrap it into a const/non-const pair and expose that in the header. That way, you pretty much only duplicate function signature.
Your functions are taking a pointer to a pidl which is either const or non-const. Either your function will be modifying the parameter or it won't - choose one and be done with it. If the function also modifies your object, make the function non-const. I don't see why you should need duplicate functions in your case.
You've got a few workarounds now...
Regarding best practices: Provide a const and a non-const versions. This is easiest to maintain and use (IMO). Provide them at the lowest levels so that it may propagate most easily. Don't make the clients cast, you're throwing implementation details, problems, and shortcomings on them. They should be able to use your classes without hacks.
I really don't know of an ideal solution... I think a keyword would ultimately be the easiest (I refuse to use a macro for it). If I need const and non-const versions (which is quite frequent), I just define it twice (as you do), and remember to keep them next to each other at all times.
I think it's hard to get around, if you look at something like vector in the STL, you have the same thing:
iterator begin() {
return (iterator(_Myfirst, this));
}
const_iterator begin() const {
return (iterator(_Myfirst, this));
}
/A.B.
During my work I developed a solution similar to what Pavel Minaev proposed. However I use it a bit differently and I think it makes the thing much simpler.
First of all you will need two meta-functions: an identity and const adding. Both can be taken from Boost if you use it (boost::mpl::identity from Boost.MPL and boost::add_const from Boost.TypeTraits). They are however (especially in this limited case) so trivial that they can be defined without referring to Boost.
EDIT: C++0x provides add_const (in type_traits header) meta-function so this solution just became a bit simpler. Visual C++ 2010 provides identity (in utility header) as well.
The definitions are following
template<typename T>
struct identity
{
typedef T type;
};
and
template<typename T>
struct add_const
{
typedef const T type;
};
Now having that generally you will provide a single implementation of a member function as a private (or protected if required somehow) static function which takes this as one of the parameters (in case of non-member function this is omitted).
That static function also has a template parameter being the meta-function for dealing with constness. Actual functions will the call this function specifying as the template argument either identity (non-const version) or add_const (const version).
Generally this will look like:
class MyClass
{
public:
Type1* fun(
Type2& arg)
{
return fun_impl<identity>(this, arg);
}
const Type1* fun(
const Type2& arg) const
{
return fun_impl<add_const>(this, arg);
}
private:
template<template<typename Type> class Constness>
static typename Constness<Type1>::type* fun_impl(
typename Constness<MyClass>::type* p_this,
typename Constness<Type2>::type& arg)
{
// Do the implementation using Constness each time constness
// of the type differs.
}
};
Note that this trick does not force you to have implementation in header file. Since fun_impl is private it should not be used outside of MyClass anyway. So you can move its definition to source file (leaving the declaration in the class to have access to class internals) and move fun definitions to source file as well.
This is only a bit more verbose however in case of longer non-trivial functions it pays off.
I think it is natural. After all you just said that you have to repeat the same algorithm (function implementation) for two different types (const one and non-const one). And that is what templates are for. For writing algorithms which work with any type satisfying some basic concepts.
I would posit that if you need to cast off the const of a variable to use it then your "consumer" code is not const correct. Can you provide a test case or two where you are running into this issue?
You don't need two versions in your case. A non-const thing will implicitly convert to a const thing, but not vice versa. From the name of you function, it looks like GetNextItem will have no reason to modify pidl, so you can rewrite it like this:
inline ITEMIDLIST * GetNextItem(const ITEMIDLIST * pidl);
Then clients can call it with a const or non-const ITEMIDLIST and it will just work:
ITEMIDLIST* item1;
const ITEMIDLIST* item2;
item1 = GetNextItem(item1);
item2 = GetNextItem(item2);
From your example, this sounds like a special case of having a pass-through function, where you want the return type to exactly match the parameter's type. One possibility would be to use a template. eg:
template<typename T> // T should be a (possibly const) ITEMIDLIST *
inline T GetNextItem(T pidl)
{
return pidl
? reinterpret_cast<T>(reinterpret_cast<const BYTE *>(pidl) + pidl->mkid.cb)
: NULL;
}
You could use templates.
template<typename T, typename U>
inline T* GetNextItem(T* pidl)
{
return pidl ? reinterpret_cast<T*>(reinterpret_cast<U*>(pidl) + pidl->mkid.cb) : NULL;
}
and use them like
ITEMDLIST* foo = GetNextItem<ITEMDLIST, BYTE>(bar);
const ITEMDLIST* constfoo = GetNextItem<const ITEMDLIST, const BYTE>(constbar);
or use some typedefs if you get fed up with typing.
If your function doesn't use a second type with the same changing constness, the compiler will deduce automatically which function to use and you can omit the template parameters.
But I think there may be a deeper problem hidden in the structure for ITEMDLIST. Is it possible to derive from ITEMDLIST? Almost forgot my win32 times... bad memories...
Edit: And you can, of course, always abuse the preprocessor. Thats what it's made for. Since you are already on win32, you can completly turn to the dark side, doesn't matter anymore ;-)
Let's say you have a class
class C
{
int * i;
public:
C(int * v):i(v) {};
void method() const; //this method does not change i
void method(); //this method changes i
}
Now you may want to define const instance of this class
const int * k = whatever;
const C c1(k); //this will fail
but this will fail because of non-const int C's constructor C(int * v)
so you define a const int constructor
C(const int * v):i(v) {}; //this will fail also
But this will fail also since C's member "int * i" is non-const.
What to do in such cases? Use mutable? Casting? Prepare const version of class?
edit: After discussion with Pavel (below) I investigated this problem a bit. To me what C++ does is not correct. Pointer target should be a strict type, that means that you could not for example do the following:
int i;
const int * ptr;
ptr = & i;
In this case language grammar treats const as a promise not to change pointer's target. In addition int * const ptr is a promise not to change pointer value itself. Thus you have two places where const can be applied. Then you may want your class to model a pointer (why not). And here things are falling into pieces. C++ grammar provides const methods which are able to promise not to change field's values itself but there is no grammar to point out that your method will not change targets of your in-class pointers.
A workaround is to define two classes const_C and C for example. It isn't a royal road however. With templates, their partial specializations it's hard not to stuck into a mess. Also all possible arguments variations like const const_C & arg, const C & arg, const_C & arg, C & arg don't look pretty. I really don't know what to do. Use separate classes or const_casts, each way seems to be wrong.
In both cases should I mark methods which don't modify pointer's target as const? Or just follow traditional path that const method doesn't change object's state itself (const method don't care about pointer target). Then in my case all methods would be const, because class is modelling a pointer thus pointer itself is T * const. But clearly some of them modify pointer's target and others do not.
Sounds like you want an object that can wrap either int* (and then behave as non-const), or int const* (and then behave as const). You can't really do it properly with a single class.
In fact, the very notion that const applied to your class should change its semantics like that is wrong - if your class models a pointer or an iterator (if it wraps a pointer, it's likely to be the case), then const applied to it should only mean that it cannot be changed itself, and should not imply anything regarding the value pointed to. You should consider following what STL does for its containers - it's precisely why it has distinct iterator and const_iterator classes, with both being distinct, but the former being implicitly convertible to the latter. As well, in STL, const iterator isn't the same as const_iterator! So just do the same.
[EDIT] Here's a tricky way to maximally reuse code between C and const_C while ensuring const-correctness throughout, and not delving into U.B. (with const_cast):
template<class T, bool IsConst>
struct pointer_to_maybe_const;
template<class T>
struct pointer_to_maybe_const<T, true> { typedef const T* type; };
template<class T>
struct pointer_to_maybe_const<T, false> { typedef T* type; };
template<bool IsConst>
struct C_fields {
typename pointer_to_maybe_const<int, IsConst>::type i;
// repeat for all fields
};
template<class Derived>
class const_C_base {
public:
int method() const { // non-mutating method example
return *self().i;
}
private:
const Derived& self() const { return *static_cast<const Derived*>(this); }
};
template<class Derived>
class C_base : public const_C_base<Derived> {
public:
int method() { // mutating method example
return ++*self().i;
}
private:
Derived& self() { return *static_cast<Derived*>(this); }
};
class const_C : public const_C_base<const_C>, private C_fields<true> {
friend class const_C_base<const_C>;
};
class C : public C_base<C>, private C_fields<false> {
friend class C_base<C>;
};
If you actually have few fields, it may be easier to duplicate them in both classes rather than going for a struct. If there are many, but they are all of the same type, then it is simpler to pass that type as a type parameter directly, and not bother with const wrapper template.
Your example doesn't fail, k is passed by value. The member i is 'implicitly constant' as direct members of C can't be changed when the instance is constant.
Constness says that you can't change members after initialization, but initializing them with values in the initialization list is of course allowed - how else would you give them a value?
What doesn't work is invoking the constructor without making it public though ;)
update addressing updated question:
Yes, C++ forces you into some verboseness sometimes, but const correctness is a common standard behaviour that you can't just redefine without breaking expectations. Pavels answer already explains one common idiom, which is used in proven libraries like the STL, for working around this situation.
Sometimes you have to just accept that languages have limitations and still deal with the expectations of the users of the interface, even if that means applying an apparently sub-optimal solution.
Your question does not make sense. Where did you get all these "this will fail" predictions? None of them are even remotely true.
Firstly, it is completely irrelevant whether the constructor's parameter is declared const or not. When you are passing by value (as in your case) you can pass a const object as an argument in any case, regardless of whether the parameter is declared as const or not.
Secondly, from the constructor's point of view, the object is NOT constant. Regardless of what kind of object you are constructing (constant or not), from within the constructor the object is never constant. So there's no need for mutable or anything.
Why don't you just try compiling your code (to see that nothing will fail), instead of making strange ungrounded predictions that something "will fail"?
A const int* is not the same as a int* const. When your class is const, you have the latter (constant pointer to mutable integer). What you're passing is the former (mutable pointer to constant integer). The two are not interchangeable, for obvious reasons.
When you instantiate
const C c1(...)
Because c1 is const, its member i turns in to:
int* const i;
As someone else mentioned, this is called implicit const.
Now, later in your example, you attempt to pass a const int*. So your constructor is basically doing this:
const int* whatever = ...;
int* const i = whatever; // error
The reason you get an error is because you can't cast const to non-const. The 'whatever' pointer is not allowed to change the thing it points to (the int part is const). The 'i' pointer is allowed to change what it points to, but cannot itself be changed (the pointer part is const).
You also mention wanting your class to model a pointer. The STL does this with iterators. The model some implementations use is having a class called 'const_iterator' which hides the real pointer and only supplies const methods to access the pointed-to data. Then there's also an 'iterator' class which inherits from 'const_iterator', adding non-const overloads. This works nicely - it's a custom class which allows the same constness as pointers, where the types mirror pointers like so:
iterator -> T*
const iterator -> T* const
const_iterator -> const T*
const const_iterator -> const T* const
Hopefully that makes sense :)
OK here's what I have done so far. To allow inheritance after const version of class without const_casts or additional space overhead I created an union which basically looks like ths:
template <typename T>
union MutatedPtr
{
protected:
const T * const_ptr;
T * ptr;
public:
/**
* Conversion constructor.
* #param ptr pointer.
*/
MutatedPtr(const T * ptr): const_ptr(ptr) {};
/**
* Conversion to T *.
*/
operator T *() {return ptr;}
/**
* Conversion to const T *.
*/
operator const T *() const {return const_ptr;}
};
When MutatedPtr field is declared, it ends up so that in const methods const_ptr is returned, while non-const ones get plain ptr. It delegates method's const-ness to pointer target which makes sense in my case.
Any comments?
BTW you can of course do similar thing with non-pointer types or even methods, so it looks that introducing mutable keyword wasn't necessary(?)
I've run into the same unfortunate issue and after lamenting the lack of a const constructor in C++ I've come to the conclusion that two templatization is the best course, at least in terms of reuse.
A very simplified version of my case/solution is:
template< typename DataPtrT >
struct BaseImage
{
BaseImage( const DataPtrT & data ) : m_data( data ) {}
DataPtrT getData() { return m_data; } // notice that if DataPtrT is const
// internally, this will return
// the same const type
DataPtrT m_data;
};
template< typename DataPtrT >
struct DerivedImage : public BaseImage<DataPtrT>
{
};
There is a very unfortunate loss of class inheritance but in my case it was acceptable to make a sort of casting operator to be able to cast between const and non-const types with some explicit knowledge of how to do the conversion under the hood. That mixed with some appropriate use of copy constructors and/or overloaded dereference operator might get you to where you want to be.
template< typename OutTypeT, typename inTypeT )
image_cast< shared_ptr<OutTypeT> >( const shared_ptr<InTypeT> & inImage )
{
return shared_ptr<OutTypeT>( new OutTypeT( inImage->getData() ) );
}