For some educational reason I managed to stop others from taking the address of my class objects through overloading the reference operator & as a deleted member function or as a private method. But C++11 presents a new templated-function std::addressof which returns the address of an object. So I want also to disable it, however I'm stuck in half-solution. Here is my code try:
#include "stdafx.h"
#include <memory>
class Foo {
public:
Foo* operator&() = delete; // declared deleted so no one can take my address
friend Foo* addressof(Foo&) = delete; // ok here.
private:
// Foo* operator&() { return nullptr; } // Or I can declare it private which conforms to older versions of C++.
};
int main() {
Foo f{};
// std::cout << &f << std::endl;
// std::cout << addressof(f) << std::endl; // ok
std::cout << std::addressof(f) << std::endl;// Why I can't stop `std::addressof()`?
std::cout << std::endl;
}
As you can see if I call addressof which is a friend template function to my class then it works fine. But if someone calls std::addressof on my class object the compiler doesn't prevent him.
I need some way to stop std::addressof to not be called on my objects.
Thank you guys.
No.
The whole point of std::addressof is to allow people to find the address of the object when the author has tried to make this difficult/obfuscated/awkward.
There is no way, provided by the language, to disable or inhibit it. This is a feature.
Speaking practically, you could possibly fake it by specialising std::addressof for your type if you don't mind your program having undefined behaviour as a result! (Seriously, don't do this…).
Related
I am reviewing some code like this, where A is a moveable type:
// Returns true exactly when ownership of a is taken
bool MaybeConsume(A&& a) {
if (some condition) {
Consume(std::move(a)); // ???
return true;
}
return false;
}
// ... elsewhere ...
A a;
if (!MaybeConsume(std::move(a))) {
a.DoSomething(); // !!!
}
Our static analysis tool complains that a is used after being moved (at !!!). IIUC std::move is only a static_cast, and the object a won't actually get gutted until a move constructor or assignment operator is called (presumably in Consume). Assuming MaybeConsume satisfies the contract in the comment,
Does this work?
Is it UB?
Is std::move at ??? a no-op?
(Probably this particular instance can be refactored to avoid the subtlety, but I would still like to ask for my own understanding).
It's a spurious warning from your static analysis tool.
Does this work?
Yes, MaybeConsume is doing what the comment says. It's only taking ownership of its argument when some condition is true (assuming Consume actually does move construct/assign from its argument).
std::move is indeed just a fancy static_cast<T&&> so MaybeConsume(std::move(a)) is not transferring ownership, you're simply binding a reference to MaybeConsume's parameter.
Is it UB?
No, you're not making use of a if MaybeConsume indicates it has assumed ownership of its argument.
Is std::move at ??? a no-op?
Well, it's a no-op because it's just a static_cast, but if you meant to ask whether it's unnecessary, then, no, it isn't. Within the body of MaybeConsume, a is an lvalue because it has a name. If the signature of Consume is void Consume(A&&), then the code won't compile without that std::move.
From the example usage you've shown, it seems you're not supposed to call MaybeConsume with a prvalue argument, since the caller should presumably use the argument in some other manner if the function returns false. If that's true, then you should change its signature to bool MaybeConsume(A&). This will probably make your static analysis tool happy because that would allow you to write if (!MaybeConsume(a)).
To understand why the static analysis tool raises a warning, one needs to think in the way a static analyzer does. When it sees a piece of code like below:
A a;
fun(std::move(a);
a.method();
It is not clear what might happen inside fun() call. To successfully perform method() on a depends some prerequisites being satisfied, that might not( or no longer) holds after call of fun(). While the programmer might well know that it is safe to call method(), the analyzer does not, so it raises a warning.
The following is only my own opinion. It is safer to just assume the ownership of a is wholly taken by fun(). To prevent confusion, it is better to enforce a borrow-and-return style, Thinking of it as if a friend borrows a book from you, you don't (can not) use that book until it is returned. Thus never risk oneself accidentally invoke an object that should be "dead" by then.
See the below demonstration code:
#include <iostream>
#include <utility>
#include <tuple>
#include<cassert>
struct A {
public:
int *p;
public:
A() {
p = new int();
assert(p != nullptr);
std::cout << p << std::endl;
std::cout << "default constrctor is called" << std::endl;
}
A(const A&) = delete;
A& operator=(const A&) = delete;
A(A&& _a): p(_a.p) {
_a.p = nullptr;
std::cout << p << std::endl;
std::cout << "move constructor is called" << std::endl;;
}
A& operator=(A&& _a) {
std::cout << "move assignment is called"<<std::endl;;
p = std::move(_a.p);
return *this;
}
void DoSomthing(){
std::cout << "do somthing is called" << std::endl;
*p = 100;
std::cout << "value of p is changed"<<std::endl;
}
};
std::tuple<A&&, bool> MaybeConsume(A&& a) {
if (1==2) {//try 1==1 alternatively
delete a.p;
a.p = nullptr;//consume
std::cout << "content consumed" << std::endl;
return std::make_tuple(Consume(std::move(a)), true);
}
else {
return std::make_tuple(std::move(a), false);
}
}
int main()
{
A a;
std::tuple<A&&, bool> t = MaybeConsume(std::move(a));
if (!(std::get<bool> (t))) {
A a1 = std::move(std::get<A&&>(t));
a1.DoSomthing();
}
return 0;
}
My c++ is rusty, so while trying to improve some code I wrote a few days ago by changing some calls from passing a MyClass *const thing to Myclass& thing, I noticed that nothing complained about code that followed this contrived example.
#include <iostream>
class Foo {
public:
Foo() {
std::cout << "foo created" << std::endl;
}
~Foo() {
std::cout << "foo destroyed" << std::endl;
}
Foo(Foo& other) {
member = other.member;
std::cout << "foo copied" << std::endl;
}
bool member = false;
};
class Bar {
public:
Bar(Foo& foo) :foo_(foo) { }
Foo foo_; // **** HERE IS THE BUG this should be: Foo& foo_;
};
int main() {
Foo foo;
Bar barOne(foo);
Bar barTwo(foo);
foo.member = true;
std::cout << barOne.foo_.member << std::endl;
std::cout << barTwo.foo_.member << std::endl;
}
I really wanted to have one Foo object, but since I forgot the & I got three instead.
foo created
foo copied
foo copied
0
0
foo destroyed
foo destroyed
foo destroyed
adding the & I get the right result.
foo created
1
1
foo destroyed
Note: the Foo, constructors and destructor are just there to demonstrate what's happening.
I know is legal, but is there a compiler flag that would warn you if you declare an Object as a member variable? Is it a bad practice to store a reference in a member variable? I would not think it is, but like I said my c++ is rusty to say the least.
Update
To answer the question of what I was refactoring from. I was doing something similar to this. I was refactoring to references as everything I read about modern c++ says to prefer references rather than pointers.
class Bar {
public:
Bar(Foo const* foo) :foo_(foo) { }
Foo const* foo_;
};
int main() {
Foo foo;
Bar barOne(&foo);
Bar barTwo(&foo);
foo.member = true;
std::cout << barOne.foo_->member << std::endl;
std::cout << barTwo.foo_->member << std::endl;
}
I know is legal, but is there a compiler flag that would warn you if you declare an Object as a member variable?
I doubt there is such a flag. Objects of one type are stored as member variables of other types too many times and too many places for that flag to be useful.
Is it a bad practice to store a reference in a member variable?
No, it is not. However, you have to be aware of where you run into problems.
As long the life of the object that holds the reference ends before the life of the object to which it holds the reference ends, you will be fine. Otherwise, you end up holding on to a dangling reference. Using a dangling reference is cause for undefined behavior.
Storing object as a member of other object is prefectly fine.
Storing reference as a member is OK if you are sure that the object holding the reference is never going to outlive the referenced variable.
foo.h
#include <iostream>
#include <memory>
class Bar
{
public:
Bar() {};
~Bar() {};
void print() {
std::cout << "hello";
}
};
class Foo
{
public:
Foo();
~Foo();
void use() {
pteste->print();
}
private:
std::unique_ptr<Bar> pteste;
};
#endif
main.cpp
#include <memory>
#include "foo.h"
int main(int argc, char *argv[])
{
Foo s;
s.use();
return 0;
}
Why and how does it works "normally"?
Thanks
EDIT: I understand about the incomplete types, but what happens when I can use unique_ptr without using new and why works
EDIT2: Organized the code better for my question
Short answer: It doesn't work.
This reference says that the default constructor of std::unique_ptr creates an empty unique pointer, meaning it has no associated object.
The reason why this code prints hello is because this statement
std::cout << "hello";
doesn't need anything of Bar. It could just as well be a static method. Maybe the compiler inlines the function and replaces s.use() with the std::cout-statement. But even if it does call the method, you won't notice any errors since it doesn't access the memory of Bar at all.
Make a slight change to your class and you will see what I mean:
class Bar
{
public:
Bar() : data(10) {};
~Bar() {};
void print() {
std::cout << "hello, data is: " << data;
}
int data;
};
Now, print accesses invalid memory, because you never called new (or even better: make_unique). It may even work and print something to the console, but the output of data will be garbage. If you're lucky, the application will crash.
Another reason why it appears to work (thanks Stas):
std::unique_ptr defines operator->, which simply returns the contained pointer, but does not check if the pointer points to valid memory. So pteste-> won't throw an exception.
Yes, this code will "normally" print "hello" to console and it is not related to unique_ptr. You can replace std::unique_ptr<Bar> pteste with Bar* pteste in Bar and get the same result.
Consider how pteste->print() is called.
You can think about Bar::print() as a free function that take pointer to Bar object:
void print(Bar* this) {
std::cout << "hello";
}
See, pointer passed to print(Bar*) is never touched, so you can theoretically pass whatever you want (null, garbage etc.) and it will print "hello" to console.
It works because
std::unique_ptr<Bar> pteste;
is a pointer declaration to the instance, it does not instantiate the pointer so it does not need to know at this point the details about Bar (e.g. ctor).
In the case of
Bar pteste
in order for pteste to be constructed it will need the know definition but since Bar is only forward declared it will give an error.
All pointers are implemented the same way. Even though you have pointers to different types, all are the size of an int usually. So the compiler does not need to know about the type of the pointee when it compiles your code. Now if you were to dereference that pointer that would be a different story. Even if you would initialize your unique_ptr it would need to know the type, since new needs to see the constructor.
Can we call an object's constructor again after it is created?
#include <iostream>
struct A
{
A ( ) { std::cout << "A::A" << std::endl; }
~A ( ) { std::cout << "A::~A" << std::endl; }
};
int main( )
{
A a;
a.~A(); // OK
a.A::A(); // OK in Visual Studio 2005, 2008, 2010
return 0;
}
You shouldn't be able to call the constructor like this, as a member function call. The reason is (n3242, 12.1/2):
A constructor is used to initialize objects of its class type. Because constructors do not have names, they are
never found during name lookup; however an explicit type conversion using the functional notation (5.2.3)
will cause a constructor to be called to initialize an object.
If you really really want to call constructor on something what should be an object - and you shouldn't do it unless in very special cases - you can use placement new that calls the constructor:
new (&a) A();
Well, a.A() fails to compile because you simply cannot call a constructor in C++. (You can invoke it indirectly, however through several means.) For the same reason, I think a.A::A() should not compile.
If your class has a reference variable, then your overloaded assignment operator needs to be written.
I was under the impression that you could only set a reference once on instantiation and therefore cannot do something like:
MyClass& MyClass::operator=(const MyClass& rhs)
{
if (&rhs != this)
{
myReference = rhs.myReference;
}
return *this;
}
How do you solve this problem?
EDIT - OK so I'm told you cannot use an assignment operator on a class with a reference, fine. But then why does visual studio let me do it? The program runs and everything.
No, you cannot re-seat a reference.
Consider:
int a = 42, b = 43;
int &ar = a;
ar = b;
How can the compiler know that you are trying to reseat ar to refer to b, and not set the value of a to 43?
You solve this "problem" by using a pointer, not a reference.
EDIT: Per your edit,
OK so I'm told you cannot use an assignment operator on a class with a
reference, fine. But then why does visual studio let me do it? The
program runs and everything.
The premise of your conclusion is wrong. You can use an assignment operator on a class which contains a reference. What you cannot do is re-seat a reference. As demonstrated in my code above, if you try to reassign a reference using ar = a; you will not re-seat what ar refers to, but change the value of what ar refers to.
Visual Studio "lets you do it," without difficulty. The misunderstanding is exactly what Visual Studio is letting you do. It's not letting you re-seat the reference. It's letting you change the value of the referant. Here is an example that I hope will clarify what this means.
#include <iostream>
#include <string>
using namespace std;
class Foo
{
public:
void dump() const
{
cout << "Foo instance " << showbase << this << "\n";
}
};
class Bar
{
public:
Bar(Foo& foo) : foo_(foo) {}
Bar& operator=(const Bar& rhs)
{
foo_ = rhs.foo_;
return * this;
}
void dump() const
{
cout << showbase << "Bar instance " << this << "\t";
foo_.dump();
}
private:
Foo& foo_;
};
int main()
{
cout << "foo1: ";
Foo foo1;
foo1.dump();
cout << "foo2: ";
Foo foo2;
foo2.dump();
cout << "bar1 :";
Bar bar1(foo1);
bar1.dump();
cout << "bar2 :";
Bar bar2(foo2);
bar2.dump();
bar2 = bar1;
cout << "bar2 after assign :";
bar2.dump();
}
The code above establishes 2 Foo objects (foo1 and foo2) and creates 2 Bar objects, each of which has a reference to a different Foo. Bar has an operator=, which executes the following:
foo_ = rhs.foo_;
If C++ allowed you to re-seat references in this way, foo_ would now refer to a different instance of Foo. But, it doesn't. This doesn't change what foo_ refers to. Instead, it calls operator= on the Foo itself. Run the above code and you'll see that the address of the Foo in bar2 never changes. If you could re-seat references, it would change.
You can make an assignment operator with a class that uses a reference, but this line:
myReference = rhs.myReference;
Does not reassign the reference. If reassigns the thing that the reference is referring to. So, after that assignment, myReference and rhs.myReference do not now refer to the same object. But the things that they refer to now have equivalent values(or whatever assignment means for that type).
If you need a reassignable reference, use a pointer. That's what they're for. In fact, in modern C++, that's pretty much the only use left for a raw pointer. If the object you're referring to is dynamically allocated, then you should put it in a shared_ptr, and make myReference either another shared_ptr, or a weak_ptr.
In two words - you can't. References have semantics of an actual object, so assignment operator will actually call an assignment for underlying object and not the reference itself.
References can't be rebound. (Well, placement new can, but DON'T DO THAT!)
But the code is legal, even though it doesn't do what you think it does. It isn't rebinding or reassigning the reference, rather assigning to the referent (the target of the reference).
Your code works as written, however you are not reassigning the reference.
myReference = rhs.myReference;
Will assign the object referenced by rhs.myReference to myReference. Therefore assuming that before the assignment &myReference != &(rhs.myReference) was true, it will still be true after the assignment, however the objects at those addresses will contain the same values (so myReference == rhs.myReference if operator== is defined for the type and works in a lets say nonsuprising way). Reassigning the reference (which is impossible) would mean that after the assignment &myReference == &(rhs.myReference) would be true. So the real question is what you want to do: Do you want to copy the object referred by rhs.myReference into the one referred by this->myReference (in which case your code is fine), or do you want to make this->myReference refer to the same object as rhs.myReference (which is not possible with references, so you would need to use pointers).