Difference between const int &x = 4 and const int x = 4 - c++

The sole purpose of references is aliasing. Assigning a reference (referring to a constant int) to a integer seems absurd since it is not an alias (and it doesn't give an error!). I suppose it is similar to defining a constant int itself. Is there any difference?

Within a function body or file scope, the only difference is decltype(x). In one case, it is int const and the other int const&.
The const int & x=7; creates a temporary anonymous int with value 7. It then binds a reference x to it. The lifetime of the temporary is the extended to that of the reference. This is basically indistinguishable from x being the name of a const int with value 7.
An exception to it being nigh identical is when the binding occurs within an object's constructor as part of member initialization. In that case, the lifetime is not extended.
I suspect you can induce this with:
struct Foo{
int const& x=7;
Foo(){};
};
Either the above syntax is illegal or it dangles (I do not recall if there is a corner case in the standard for references), while:
struct Foo{
int const x=7;
Foo(){};
};
is both legal and does not dangle. So there is a difference.
There would also be a difference as a parameter to a function, wbhere =7 simply provides a default.

Related

Are some usages of const only really useful when returning or passing things by reference? Or do they have subtle uses I'm not seeing

I've read about the various places to put const. Some usages seem clearly useful to me. Others however evade me. It would be really helpful if someone could confirm or correct my understanding as I explain my mental model of things.
These are the same. I'm not sure I understand why this would ever be useful, though. Does it perhaps allow one to initialize const int variables with a function, which in turn allows some compiler optimizations?
const int foo();
int const foo();
These are the same. The returned pointer cannot be used (via dereferencing) to change the values pointed to.
const int * foo();
int const * foo();
This means the returned pointer itself cannot be changed. But, why would it matter if the caller essentially decides to ignore the returned pointer and set it to something else? Is this only really useful if the pointer is returned by reference?
int * const foo();
These are the same. It means you can only pass in const ints, which allows the compiler to optimize things.
int foo(const int foo);
int foo(int const foo);
This means the passed-in pointer cannot be changed. I'm wondering here too, why would it matter unless the pointer is being passed in by reference?
int foo(int * const foo);
This (as a member function) guarantees that the function won't change the state of the object. Also, if the object itself is declared const, then it will only be able to call such functions.
int foo(int foo) const;
const int as return type is pointless, because in an expression the type of a non-class prvalue result of a function call will be stripped of its top-level const anyway. There is no distinction between a const and non-const prvalue of a non-class type. So the type of foo() is just int, no matter whether it is declared to return const int or int.
It would be different if the return type was a const qualified class type, e.g. const std::string foo(). In that case the const would disallow calling non-const-qualified member functions directly on the result of foo(), e.g. foo().resize(42). Still, this is a very rarely used distinction. And as noted in the comments, under certain conditions it can prevent move operations. E.g. in the above if we have a std::vector<std::string> v;, then v.push_back(foo()) will cause a copy, rather than a move, of the returned string into the vector.
However, the const qualifier is part of the return type in the function type and therefore it is technically possible to differentiate a function declared with const return type from one without it. The type of int foo(int foo) is int(int), but the type of const int foo(int foo) is const int(int). (However overloading based on return type is not possible for non-template functions anyway. The return type is not part of the function signature.)
correct
Same as 1. The type of foo() is simply int*.
The top-level const in the function parameter does not affect the type or signature of the function (in contrast to 1. where it does affect the type). So int foo(const int foo); and int foo(int foo); declare the same function and both have type int(int). Top-level const also doesn't affect how a variable can be initialized, so it doesn't make sense to say "you can only pass in const int". There are no const-qualified prvalues of type int anyway and if int foo can be initialized from some expression, then so can const int foo. The const has no implication on initialization or overload resolution. However const can be used like this in a function definition to tell the compiler and yourself that this parameter is not intended to be modified in the definition.
Same as 4.
This is the correct idea, although in the details it is not strictly true. Rather the const is only relevant to overload resolution (behaving as if the implicit object parameter was a const reference) and the type of this (which will be a pointer to const). It is still possible to mutate members declared as mutable or to use const_cast to mutate members. It is also not relevant whether the object itself is const, only whether the glvalue through which the member function is called is.

Does const allow for (theoretical) optimization here?

Consider this snippet:
void foo(const int&);
int bar();
int test1()
{
int x = bar();
int y = x;
foo(x);
return x - y;
}
int test2()
{
const int x = bar();
const int y = x;
foo(x);
return x - y;
}
In my understanding of the standard, neither x nor y are allowed to be changed by foo in test2, whereas they could be changed by foo in test1 (with e.g. a const_cast to remove const from the const int& because the referenced objects aren't actually const in test1).
Now, neither gcc nor clang nor MSVC seem to optimize test2 to foo(bar()); return 0;, and I can understand that they do not want to waste optimization passes on an optimization that only rarely applies in practice.
But am I at least correct in my understanding of this situation, or am I missing some legal way for x to be modified in test2?
The standard says in [dcl.type.cv]:
Except that any class member declared mutable […] can be modified, any attempt to modify […] a const object […] during its lifetime […] results in undefined behavior.
It is also not possible to make this defined by ending the lifetime of the object prematurely, according to [basic.life]:
Creating a new object within the storage that a const complete object with […] automatic storage duration occupies, or within the storage that such a const object used to occupy before its lifetime ended, results in undefined behavior.
This means that the optimization of x - y to zero is valid because any attempt to modify x in foo would result in undefined behavior.
The interesting question is if there is a reason for not performing this optimization in existing compilers. Considering that the const object definition is local to test2 and the fact is used within the same function, usual exceptions such as support for symbol interposition do not apply here.

Is modifying non-mutable member of non-const object in const method Undefined Behaviour?

dcl.type.cv provides an interesting example:
For another example,
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
which indicates that, via const_cast, one may modify mutable members of a const qualified object, while you can't do that with non-mutable members.
To my understanding, this is because of the original constness of y itself. What would happen if we got rid of the mutable keyword, the const qualifier fot y, but modified the fields in a const method?
Example below:
#include <vector>
struct foo {
std::vector<int> vec{};
void bar() const {
auto& raw_ref = const_cast<std::vector<int>&>(vec);
raw_ref.push_back(0); // ok?
auto* raw_this = const_cast<foo*>(this);
raw_this->vec.push_back(0); // ok?
}
};
int main() {
foo f{};
f.bar();
}
Does it exhibit Undefined Behaviour? I would think that it does not, since we're modifying an originally non-const, but in a const context.
Additionally, notice that I provided two ways of modifying the vec. One with non-const reference and one with non-const pointer to this (which was originally const in this constext due to foo::bar being a const method). Do they differ in any particular way, given the question's context? I would assume that both are okay here.
Disclaimer: I am aware of mutable keyword, but this desing (not only being flawed) is simply an example. One could assume that the author of the code wanted to prohibit every single way of modifying vec except for push_backs.
Your quoted paragraph actually spelled out exactly what is undefined [dcl.type.cv]
Except that any class member declared mutable can be modified, any attempt to modify a const object during its lifetime results in undefined behavior.
A const reference/pointer to a non-const object doesn't make that object a const object, all your accesses are well-formed.

Temporary mutability of a member variable

Is it possible to have a member variable only be considered mutable for a given function/code block?
e.g.
class Foo() {
int blah;
void bar() const {
blah = 5; // compiler error
}
void mutable_bar() const {
blah = 5; // no compiler error
}
}
note: in this case I do NOT want to get rid of the const at mutable_bar since logical const will be preserved.
Same question, but different perspective: Can I somehow apply the mutable keyword to a method instead of a variable?
No, it is not possible, at least in C++. You need either mutable or non const function.
Also there is const_cast don't use it to modify things. In case if you modify const_casted const value you get Undefined Behaviour.
5.2.11 Const cast
7 [ Note: Depending on the type of the object, a write operation through the pointer, lvalue or pointer
to data member resulting from a const_cast that casts away a const-qualifier73 may produce undefined
behavior (7.1.6.1). —end note ]
7.1.6.1 The cv-qualifiers
4 Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const
object during its lifetime (3.8) results in undefined behavior.
....
5 For another example
struct X {
mutable int i;
int j;
};
struct Y {
X x;
Y();
};
const Y y;
y.x.i++; // well-formed: mutable member can be modified
y.x.j++; // ill-formed: const-qualified member modified
Y* p = const_cast<Y*>(&y); // cast away const-ness of y
p->x.i = 99; // well-formed: mutable member can be modified
p->x.j = 99; // undefined: modifies a const member
—end example ]
It is technically possible to bypass the const in this case, by for example:
void mutable_bar() const {
int& p_blah = const_cast<int&>(blah);
p_blah = 5; // no compiler error
}
Or some similar construct. But you are really jumping through hoops to do something that you shouldn't be able to do. And as a comment on another post says, this is "undefined behavior", which means that in some cases it may not even work (or do what you expect it to do).
You could use a const_cast to make the member not-const in selected cases. This, along with an according comment, might even be a relatively clean solution. At least it's explicit that you break the const in a restricted scope, instead of making it world-wide mutable.

Reference to global functor in another global functor

The following code yields a Segmentation Fault on the y = anotherFunctor() line. As far as I understand, this happens because the globalFunctor variable does not exist when anotherFunctor is created. But why does it work if I replace std::function<int(int)> with GlobalFunctor? How would I fix it?
#include <functional>
struct GlobalFunctor
{
int operator()() const { return 42; }
};
extern GlobalFunctor globalFunctor;
struct AnotherFunctor
{
AnotherFunctor() : g_(globalFunctor) {}
int operator()() const { return g_(); }
const std::function<int()>& g_;
} anotherFunctor;
GlobalFunctor globalFunctor;
int main()
{
AnotherFunctor af;
int x = af();
int y = anotherFunctor();
int z = x + y;
return 0;
}
Edit: I tried compiling this with clang instead of gcc and it warns me about binding reference member 'g_' to a temporary value -- but it crashes when compiling this. Would the cast to std::function create a temporary reference?
At g_(globalFunctor), globalFunctor has to be converted to an std::function because it is of type GlobalFunctor. So a temporary is produced and this is bound to the constant reference. You could think of the code as doing g_(std::function<int()>(globalFunctor)). However, this temporary only lives until the end of the constructor, as there is a special rule in C++ saying that temporaries in member initializer lists only live until the end of the constructor. This leaves you with a dangling reference.
The code works when you replace std::function<int(int)> with GlobalFunctor because no conversion is involved. Therefore, no temporaries are produced and the reference directly refers to the global object.
You either need to not use references and store a std::function internally or make a global std::function and have a reference to that.