First let me ask a general question:
The crux of the C++ language's design is that it provides abstraction at very little (often zero) cost. Using classes is no more expensive than providing a struct and related functions in C, but the compiler can enforce restrictions on your code such that the semantic benefits of classes are realised. This is just as true for templates, references, lambdas, copy/move semantics, strings, I/O, etc. You don't pay for what you don't use and, when you do use something, you pay very little.
Another important design principle of C++ is that it doesn't provide language features that can't just be easily built from other language features or that don't provide a useful form of abstraction for the author. Each language feature is a building block that can be used to make a fully expressive program. This aids flexibility. If the language were to provide the feature you're looking for, it would be reducing that flexibility. If you want the state of your object to be const
regardless of how the client wants to use it, you need to make every member of your object const
- the members are your object's state. C++ doesn't want to give you absolutely everything; it just gives you the necessary tools to write fully expressive code with minimal cost.
I have been in this situation a few times.
That is,
I designed a class that due to unacceptable performance requirements, it was basically immutable.
Immutable in the sense that its instance can defined at construction and never modified.
In effect, all its member function were const
.
(Well, there is a side question as it is whether it can be assignable still, but let's go on).
It would be nice to be able to force the use to declare the instances const
to make the intent more evident.
I don't think C++ supports this without adding a level of indirection, that is to access the object through a const pointer and disabling direct construction. (Or even better a unique_ptr
).
struct A{ // this class is immutable, so in princple `A a` and `A const a` should behave the same
int f() const{return 5;}
private:
A(){}
public:
template<class... Args>
static A const* make_const(Args&&... args){return new A(std::forward<Args>(args)...);}
};
int main(){
std::unique_ptr<A const> a(A::make_const());
std::cout << a->f() << std::endl;
}
Note that the following doesn't compile, as intended:
A a; // doesn't compile because the constructors are private
nor does
std::unique<A> a(A::make_const()); // make creates pointers to const, so this doesn't compile.
Only the following compiles, and effectively you are mandating const
:
std::unique_ptr<A const> a(A::make());
One can make it better by using std::indirect
http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0201r1.pdf and in the future overloading operator.
will also help.
Of course a much cheaper alternative is to give the name of the class have const
in the name like struct A_const{...
or struct cA{...
to hint that only const
makes sense.
(Perhaps, If you decide to have mutating members later, you can inherit from this, struct A : A_const{...
).
Just create your instance of the class as const
. This will ensure all the internals are const
.
E.g. const MY_TYPE obj;
Why doesn't C++ seem to support creating classes whose objects can be only const? In other words, classes where you get a compile-time error if you try to create a non-const object of it. This seems like a natural thing to do in many cases, yet C++ doesn't allow it. What is the reason behind this?
The first question that comes to mind is when having a type for which all objects are const
is a natural thing. There are types that are naturally immutable, in some languages it's even recommended to make as many types as possible immutable, but that is not the same thing as enforcing that all objects are const
.
In your class design it is trivial to control whether any of your member functions modifies the state of the object achieving an immutable object. Encapsulation will then do its magic, and if you don't expose any mutating function, then the type is immutable and as good as const
. You can also (if you want the extra safety) qualify all your member data as const
to let the compiler tell you if you violate your own constraint in your own code. Note that forcing all the objects to be const
implicitly would mark all your member data to be const
, and your non-const functions useless.
Given that the high level effect can be achieved in different ways with little cost, why would you make a complex language even more complicated for no added value?
While the question above may seem rhetoric it is not. One of the criteria to consider extensions to the language is the return on investment: What will we be able to do with this feature that was not possible without it? (i.e. is this an enabling feature?) If the answer is nothing, then the next question is How much simpler will programmer's life be with this feature compared to the current state? (for non-enabling features, what is the value of adding them?)
In the enabling category the example would be rvalue-references; without that language feature, you cannot implement perfect-forwarding, nor move-semantics. In the second category you can consider lambdas — there is nothing that can be done with lambdas that was not doable without them — whose value lies on simplifying user code (code that would have to be convoluted: create a functor, declare all members of the proper types, provide a constructor that initializes — does the capture — the members…) but the code becomes much simpler with lambdas. Note that it took quite a bit of arguing from Sutter to convince Stroustrup that lambdas had value.
In your case, what you are asking for is not an enabling feature, and the cost of supporting the use cases without the feature is low enough that it does not grant providing that as a core language feature.
Your first paragraph details how C++ is flexible and allows you to express many different programs.
Your second paragraph asks why C++ doesn't allow a programmer to enforce a highly stringent limitation that is of questionable benefit (when you can simply mark all the members const
to achieve almost the same result).
Let us each draw our own conclusions from this dichotomy. :)