Is the following C++ code equiv? (in a smart pointer implementation) - c++

Code 1:
template<class T>
const PtrInterface<T>*
PtrInterface<T>::newRef() const {
PtrInterface<T>* me = (PtrInterface<T>*) this;
++me->references_;
//++this->references_;
return this;
}
Code 2:
template<class T>
const PtrInterface<T>*
PtrInterface<T>::newRef() const {
//PtrInterface<T>* me = (PtrInterface<T>*) this;
//++me->references_;
++this->references_;
return this;
}
Is there ever any situation where these two blocks of code will do different things?
Thanks!

Is there ever any situation where these two blocks of code will do different things?
Yes, when you are in a const method. Currently, the one with me invokes undefined behavior. Here's why:
As you know, when you call a member function, there is an implicit this pointer. The this pointer is const when a function is marked const. Take this for example:
struct foo
{
void method1(void);
void method2(void) const;
int i;
};
Implicitly, the compiler generates (by the way, this is simplified):
void foo::method1(foo* this);
void foo::method2(const foo* this) const;
So, are these two bodies the same?
foo* me = (foo*)this;
me->i = 1;
// and
this->i = 1;
The answer is it depends, and as stated earlier, it's dependent on the const-ness of the function. In a non-const function, they are the same:
void foo::method1(foo* this)
{
foo* me = (foo*)this; // this cast is redundant
me->i = 1;
// ...
this->i = 1;
}
But in a const function:
void foo::method2(const foo* this) const
{
foo* me = (foo*)this; // uh-oh! acts like const_cast
me->i = 1; // modifying a const_cast'd variable is undefined behavior
// ...
this->i = 1; // wouldn't compile
}
We end up stripping the const away. So, no, they aren't always the same. This is the peril of the C-style cast: it will find a way. By the way, casting const away in itself isn't undefined behavior; it's the modification of said variable that does it.
There is a sticky problem in your question though: your code shouldn't compile. Like in the commented code above, in your const method you shouldn't be able to modify reference_.
This is different if reference_ is mutable, which I'm guessing it might be (assuming you gave us compilable code.) In this case, I'm not certain if the first sample leads to undefined behavior, since it was mutable in the first place. I wouldn't take the chance though.

More or less everything GMan has said, except references_ need not be mutable. It could also be an object which has overridden operator++() to be a const member function.

Related

Allowing a function to mutate a const object's member variable

This is related to the (currently) closed question I asked earlier: Can you mutate an object of custom type when it's declared as constant?
Suppose we have something that looks like the following:
class test
{
public:
test() : i{4}, ptr{&i} {};
int i;
int *ptr;
int *get_ptr() const {return ptr;}
};
void func(const test &t, int j) {
*(t.get_ptr()) = j;
// auto ptr = t.get_ptr();
// *ptr = j;
}
int main(int argc, char const *argv[])
{
test t;
std::cout << t.i << std::endl;
func(t, 5);
std::cout << t.i << std::endl;
}
We have this func that takes in a const test &. When I see this signature (and if I didn't look at the implementation of the function), it makes me want to assume that nothing in t will get modified; however, the member variable i is able to be modified through the ptr member variable, as we see here.
I don't usually write code that end up working this way, so I'm wondering if code like this is discouraged?
Furthermore, is it reasonable to assume (most of the time) that an object declared as const will not be mutated?
Yes code like this is definitely discouraged. It completely ignores the reason we have the const keyword in the first place. The function is deliberately modifying something that it is advertising it will not modify
That means that whoever wrote the get_ptr() function messed up because they declared it const but let it return a pointer to non-const object (so one that can be changed, defeating the purpose of declaring the function const)
If whatever get_ptr() returns could properly be modified by such a function then it should be an implementation detail, a private (or protected) variable marked with the mutable keyword.
test::get_ptr() should have two overloads.
const int* get_ptr() const { return ptr; }
int* get_ptr() { return ptr; }
If func() wants to change the test object given to it then it should take test&, not const test&
The key is here:
int *get_ptr() const {return ptr;}
You define get_ptr as const which is akin to saying "get_ptr is not going to change any attributes of the class". And it doesn't. It returns the value of the attribute ptr, which is a int*, and points to a mutable instance of an int which happens to be an attribute of the class as well.
The compiler has no way of knowing this, so from the compiler's perspective the promise was kept. However in reality you're circumventing the const qualifier and allowing to mutate an otherwise immutable attribute.
Not the best of coding practices, but whoever writes such code should, obviously, not expect for i to remain as it was set in the class methods if get_ptr is ever called.

Is it legal to cast const away of a non-static const field with guaranteed non const allocation

I have the following code which seems to work always (msvc, gcc and clang).
But I'm not sure if it is really legal. In my framework my classes may have "two constructors" - one normal C++ constructor which does simple member initialization and an additional member function "Ctor" which executes additional initialization code. It is used to allow for example calls to virtual functions. These calls are handled by a generic allocation/construction function - something like "make_shared".
The code:
#include <iostream>
class Foo
{
public:
constexpr Foo() : someConstField(){}
public:
inline void Ctor(int i)
{
//use Ctor as real constructor to allow for example calls to virtual functions
const_cast<int&>(this->someConstField) = i;
}
public:
const int someConstField;
};
int main()
{
//done by a generic allocation function
Foo f;
f.Ctor(12); //after this call someConstField is really const!
//
std::cout << f.someConstField;
}
Modifying const memory is undefined behaviour. Here that int has already been allocated in const memory by the default constructor.
Honestly I am not sure why you want to do this in the first place. If you want to be able to initalise Foo with an int just create an overloaded constructor:
...
constexpr Foo(int i) : someConstField{i} {}
This is completely legal, you are initalising the const memory when it is created and all is good.
If for some reason you want to have your object initalised in two stages (which without a factory function is not a good idea) then you cannot, and should not, use a const member variable. After all, if it could change after the object was created then it would no longer be const.
As a general rule of thumb you shouldn't have const member variables since it causes lots of problems with, for example, moving an object.
When I say "const memory" here, what I mean is const qualified memory by the rules of the language. So while the memory itself may or may not be writable at the machine level, it really doesn't matter since the compiler will do whatever it likes (generally it just ignores any writes to that memory but this is UB so it could do literally anything).
No.
It is undefined behaviour to modify a const value. The const_cast itself is fine, it's the modification that's the problem.
According to 7.1.6.1 in C++17 standard
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.
And there is an example (similar to yours, except not for class member):
const int* ciq = new const int (3); // initialized as required
int* iq = const_cast<int*>(ciq); // cast required
*iq = 4; // undefined: modifies a const object
If your allocation function allocates raw memory, you can use placement new to construct an object at that memory location. With this you must remember to call the destructor of the object before freeing the allocation.
Small example using malloc:
class Foo
{
public:
constexpr Foo(int i) : someConstField(i){}
public:
const int someConstField;
};
int main()
{
void *raw_memory = std::malloc(sizeof(Foo));
Foo *foo = new (raw_memory) Foo{3}; // foo->someConstField == 3
// ...
foo->~Foo();
std::free(foo);
}
I suggest, that you use the constructor to avoid the const cast. You commented, that after your call of Ctor the value of someConstField will remain const. Just set it in the constructor and you will have no problems and your code becomes more readable.
#include <iostream>
class Foo
{
public:
constexpr Foo(int i) : someConstField(Ctor(i)){}
int Ctor(); // to be defined in the implementation
const int someConstField;
};
int main()
{
Foo f(12);
std::cout << f.someConstField;
}

const_cast 'this' in const method to assign 'this' to outer variable?

Have a look at the following code:
struct Foo;
Foo* bar;
struct Foo {
void func() const {
bar = this;
}
}
int main() {
Foo().func();
}
This does not work as bar won't accept a const Foo*. To get around this, const_cast could be used:
struct Foo {
void func() const {
bar = const_cast<Foo*>(this);
}
}
Is this safe? I'm very cautious when it comes to using const_cast, but in this case it seems legit to me.
No, this is potentially dangerous.
func() is marked const which means that it can be called by a const object:
const Foo foo;
foo.func();
Because this is const Foo*.
If you const_cast away the const you end up with a Foo* to a const object. This means that any modification to that object through the non-const pointer (or through any copy of that pointer, bar in this case) will get you undefined behavior since you are not allowed to modify a const object (duh).
Plus the obvious problem is that you're lying to the consumer of class Foo by saying func() won't modify anything while you're doing the opposite.
const_cast is almost never correct and this seems like an XY-problem to me.
If by "safe" you mean "not undefined behavior" then yes, it is safe. The value of bar will point to the created object as you'd expect.
However, const_cast is generally not recommended because it breaks the convention that a const thing will not be changed, and can easily produce undefined behavior (see this comment below). In this case you can simply do:
struct Foo {
void func() const {
bar = const_cast<Foo*>(this);
bar->modify();
}
void modify() { ... }
}
And you will be modifying the object in a const method, which is unexpected in general and undefined behavior if the instance of Foo on which the method is called was initially declared as const. However, it is up to you to get the logic right. Just note that everyone else will expect const things to be const, including the standard library, and usually (if not always) a better code design is possible.
Under some circumstances it is safe, namely when you have a non-const Foo object. If you have a const Foo object however, you're not allowed to modify it, and the compiler will not catch the bug because of the cast. So it is not a good idea to use this.
Note that in your example Foo is a temporary which gets destroyed on the next line of main().

C++: is return value a L-value?

Consider this code:
struct foo
{
int a;
};
foo q() { foo f; f.a =4; return f;}
int main()
{
foo i;
i.a = 5;
q() = i;
}
No compiler complains about it, even Clang. Why q() = ... line is correct?
No, the return value of a function is an l-value if and only if it is a reference (C++03). (5.2.2 [expr.call] / 10)
If the type returned were a basic type then this would be a compile error. (5.17 [expr.ass] / 1)
The reason that this works is that you are allowed to call member functions (even non-const member functions) on r-values of class type and the assignment of foo is an implementation defined member function: foo& foo::operator=(const foo&). The restrictions for operators in clause 5 only apply to built-in operators, (5 [expr] / 3), if overload resolution selects an overloaded function call for an operator then the restrictions for that function call apply instead.
This is why it is sometimes recommended to return objects of class type as const objects (e.g. const foo q();), however this can have a negative impact in C++0x where it can inhibit move semantics from working as they should.
Because structs can be assigned to, and your q() returns a copy of struct foo so its assigning the returned struct to the value provided.
This doesn't really do anything in this case thought because the struct falls out of scope afterwards and you don't keep a reference to it in the first place so you couldn't do anything with it anyway (in this specific code).
This makes more sense (though still not really a "best practice")
struct foo
{
int a;
};
foo* q() { foo *f = new malloc(sizeof(foo)); f->a = 4; return f; }
int main()
{
foo i;
i.a = 5;
//sets the contents of the newly created foo
//to the contents of your i variable
(*(q())) = i;
}
One interesting application of this:
void f(const std::string& x);
std::string g() { return "<tag>"; }
...
f(g() += "</tag>");
Here, g() += modifies the temporary, which may be faster that creating an additional temporary with + because the heap allocated for g()'s return value may already have enough spare capacity to accommodate </tag>.
See it run at ideone.com with GCC / C++11.
Now, which computing novice said something about optimisations and evil...? ;-].
On top of other good answers, I'd like to point out that std::tie works on top of this mechanism for unpacking data from another function. See here. So it's not error-prone per se, just keep in mind that it could be a useful design pattern

Convert "this" to a reference-to-pointer

Let's say I have a struct
struct Foo {
void bar () {
do_baz(this);
}
/* See edit below
void do_baz(Foo*& pFoo) {
pFoo->p_sub_foo = new Foo; // for example
}
*/
Foo* p_sub_foo;
}
GCC tells me that
temp.cpp: In member function ‘void Foo::bar()’:
temp.cpp:3: error: no matching function for call to ‘Foo::do_baz(Foo* const)’
temp.cpp:5: note: candidates are: void Foo::do_baz(Foo*&)
So, how do I convert what is apparently a const Foo* to a Foo*&?
EDIT: I didn't use a very good example. do_baz should read
void do_baz(Foo*& pFoo) {
if (pFoo == NULL) {
pFoo = new Foo;
return;
}
//other stuff
do_baz(pFoo->p_sub_foo);
//more stuff
}
You can't.
Firstly, this is not necessarily a const Foo *. this would be a const Foo * is a const method of the class Foo. In a non-const method this is just Foo *. (Actually your error message mentions Foo* const. Where did you see const Foo *?)
Secondly, and more importantly, this is not an lvalue. You can't have a pointer to this. You can't have a non-constant reference to this. The only thing that you can have is a const reverence to this, i.e. a reference of type Foo *const &.
It (Foo *const &) will work in your case.
void do_baz(Foo* const& pFoo) {
pFoo->p_sub_foo = new Foo;
}
But I don't see the point of all this. Just declare a normal Foo * pointer as parameter for your do_baz method
void do_baz(Foo* pFoo) {
pFoo->p_sub_foo = new Foo;
}
and get the same result. What do you think you need that reference for?
EDIT: Taking into account your edit, what you are trying to do cannot be done with a single do_baz function, since in the first call you'd potentially (semantically) attempt to modify this, which is impossible (even if the modifying code will never be executed in practice). Whether you want it or not, you can't have a non-const reference to this, even if you don't intend to write anything through it. You'll probably have to implement the very first call with a different function
void do_baz(Foo*& pFoo) {
if (pFoo == NULL) {
pFoo = new Foo;
return;
}
//other stuff
do_baz(pFoo->p_sub_foo);
//more stuff
}
void do_baz_root(Foo* pFoo) {
assert(pFoo != NULL);
//other stuff
do_baz(pFoo->p_sub_foo);
//more stuff
}
and then make the first call as
void bar() {
do_baz_root(this);
}
Give the variable a name, and then you will have a pointer reference type:
void bar () {
Foo* me = this;
do_baz(me);
}
I should also point out that your do_baz function isn't making use of the fact that its parameter is a reference (you are not assigning to the pointer, itself, only to what is being pointed to by the pointer). Consequently, it really makes more sense to change the type of the parameter to Foo* instead of Foo*&, or to make it a Foo&, in which case you would use dot (.) instead of arrow (->) when dereferencing the parameter's member.
Edit
Your new version of do_baz now makes use of the fact that the parameter is a reference. The solution above (simply using a named pointer) will still work for your new version of the problem. That said, I would advise against what you are doing. It seems you are trying to insert an element at the end of a linked list...
Firstly, I would advise that if you are implementing a linked list that you maintain not only a pointer to the first node in the list, but also a pointer to the last node in the list at all times, so that insertion at the end of the list may be performed in constant-time. If, however, that is not a possibility, I would nevertheless advise you to use an iterative implementation rather than a recursive one as it is cleaner and simpler. It would look like:
Foo* current=this;
while (current->next != NULL){
current=current->next;
}
current->next = new Foo;
You don't.
this is a Foo* const, meaning it is a const pointer to a non-const Foo. Your reference is non-const, so the correct declaration would be a Foo* const &.
But it doesn't make any sense to do this, so don't.
The keyword this is not an lvalue so this can't be assigned to/changed (regardless of whether what it points to is const or not). In other words, you might be able to change what this points to, but you can't change the value of this itself. The C++ standard 9.3.2 "The this pointer":
In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression
Only const references can bind to non-lvalue objects, so you'd need to bind it to a Foo* const& if you want to bind the this pointer to a reference.
Change the signature of the function to:
void do_baz(Foo* const& pFoo);