Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 days ago.
Improve this question
This question is different from std::shared_ptr which is empty but not null.
That question is about solving a particular problem, while I'm asking about the rationale and design reasons behind a certain standard feature.
This question is a predecessor to another question: Why is empty weak_ptr required to store null pointer, while empty shared_ptr is allowed to store non-null pointer? They share the same context, to some extent. However, I believe this question is self-contained and answerable in itself.
The C++ standard offers an aliasing constructor for shared_ptr:
template <typename T>
class shared_ptr
{
template <typename U>
shared_ptr(const shared_ptr<U>& r, element_type* ptr);
};
Such constructor means that a shared_ptr object can be created in a way that it owns one object, same as owned by r, while at the same time storing a pointer to another object -- ptr.
Also, the standard explicitly allows using this constructor to create a shared_ptr which doesn't own anything (thus meeting the definition of 'empty'), but still points to some object (thus qualified as 'non-null'):
[util.smartptr.shared.const]:
17. [Note 2: This constructor allows creation of an empty shared_ptr instance with a non-null stored pointer. — end note]
Construction of an empty but non-null shared_ptr could have been easily forbidden, for example by demanding that attempted construction of an empty shared_ptr instance with a non-null stored pointer should throw an exception.
But the standard chose to allow it. I wonder why and what for.
What is the intended and canonical use scenario for an empty but non-null shared_ptr?
The aliasing constructor was added to std::shared_ptr during standardization, in N2351 "Improving shared_ptr for C++0x, Revision 2":
This feature extends the interface of shared_ptr in a backward-compatible way that increases its expressive power and is therefore strongly recommended to be added to the C++0x standard. It introduces no source- and binary compatibility issues.
At this time it was already noted that:
[Note: This constructor allows creation of an empty shared_ptr instance with a non-NULL stored pointer. --end note.]
The aliasing constructor was at the same time added to boost::shared_ptr, which acted as a testbed: https://github.com/boostorg/smart_ptr/commit/54e12d03fdfec63b4d8ff41991c4e64af6b1b4b4 https://github.com/boostorg/smart_ptr/commit/ce72827dc73ac652ed07002b75f32e0171119c09
As for why this is permitted: there is a clear alternative, which is for the user to construct a shared_ptr from an existing pointer and a no-op deleter:
auto sp = shared_ptr(p, [](auto){})
However, this is less efficient than aliasing the empty state (since a separate control block must be allocated) and less expressive, since it is not possible to determine that the deleter is a no-op. So there is little reason to forbid the former.
Related
I was reading Top 10 dumb mistakes to avoid with C++11 smart pointer.
Number #5 reads:
Mistake # 5 : Not assigning an object(raw pointer) to a shared_ptr as
soon as it is created !
int main()
{
Aircraft* myAircraft = new Aircraft("F-16");
shared_ptr<aircraft> pAircraft(myAircraft);
...
shared_ptr<aircraft> p2(myAircraft);
// will do a double delete and possibly crash
}
and the recommendation is something like:
Use make_shared or new and immediately construct the pointer with
it.
Ok, no doubt about it the problem and the recommendation.
However I have a question about the design of shared_ptr.
This is a very easy mistake to make and the whole "safe" design of shared_ptr could be thrown away by very easy-to-detect missuses.
Now the question is, could this be easily been fixed with an alternative design of shared_ptr in which the only constructor from raw pointer would be that from a r-value reference?
template<class T>
struct shared_ptr{
shared_ptr(T*&& t){...basically current implementation...}
shared_ptr(T* t) = delete; // this is to...
shared_ptr(T* const& t) = delete; // ... illustrate the point.
shared_ptr(T*& t) = delete;
...
};
In this way shared_ptr could be only initialized from the result of new or some factory function.
Is this an underexploitation of the C++ language in the library?
or What is the point of having a constructor from raw pointer (l-value) reference if this is going to be most likely a misuse?
Is this a historical accident? (e.g. shared_ptr was proposed before r-value references were introduced, etc) Backwards compatibility?
(Of course one could say std::shared_ptr<type>(std::move(ptr)); that that is easier to catch and also a work around if this is really necessary.)
Am I missing something?
Pointers are very easy to copy. Even if you restrict to r-value reference you can sill easily make copies (like when you pass a pointer as a function parameter) which will invalidate the safety setup. Moreover you will run into problems in templates where you can easily have T* const or T*& as a type and you get type mismatches.
So you are proposing to create more restrictions without significant safety gains, which is likely why it was not in the standard to begin with.
The point of make_shared is to atomize the construction of a shared pointer. Say you have f(shared_ptr<int>(new int(5)), throw_some_exception()). The order of parameter invokation is not guaranteed by the standard. The compiler is allowed to create a new int, run throw_some_exception and then construct the shared_ptr which means that you could leak the int (if throw_some_exception actually throws an exception). make_shared just creates the object and the shared pointer inside itself, which doesn't allow the compiler to change the order, so it becomes safe.
I do not have any special insight into the design of shared_ptr, but I think the most likely explanation is that the timelines involved made this impossible:
The shared_ptr was introduced at the same time as rvalue-references, in C++11. The shared_ptr already had a working reference implementation in boost, so it could be expected to be added to standard libraries relatively quickly.
If the constructor for shared_ptr had only supported construction from rvalue references, it would have been unusable until the compiler had also implemented support for rvalue references.
And at that time, compiler and standards development was much more asynchronous, so it could have taken years until all compiler had implemented support, if at all. (export templates were still fresh on peoples minds in 2011)
Additionally, I assume the standards committee would have felt uncomfortable standardizing an API that did not have a reference implementation, and could not even get one until after the standard was published.
There's a number of cases in which you may not be able to call make_shared(). For example, your code may not be responsible for allocating and constructing the class in question. The following paradigm (private constructors + factory functions) is used in some C++ code bases for a variety of reasons:
struct A {
private:
A();
};
A* A_factory();
In this case, if you wanted to stick the A* you get from A_factory() into a shared_ptr<>, you'd have to use the constructor which takes a raw pointer instead of make_shared().
Off the top of my head, some other examples:
You want to get aligned memory for your type using posix_memalign() and then store it in a shared_ptr<> with a custom deleter that calls free() (this use case will go away soon when we add aligned allocation to the language!).
You want to stick a pointer to a memory-mapped region created with mmap() into a shared_ptr<> with a custom deleter that calls munmap() (this use case will go away when we get a standardized facility for shmem, something I'm hoping to work on in the next few months).
You want to stick a pointer allocated by into a shared_ptr<> with a custom deleter.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Is the following code safe?
struct Foo {
Foo bar() { return *this; }
} foo;
foo = std::move(foo).bar(); // is this safe?
I'm wondering if calling methods on an rvalue is safe.
In general, calling a function on an rvalue is no more or less safe than calling it on an lvalue.
As for your specific code, no movement actually happens. std::move(foo) converts it into an rvalue, which is irrelevant to the call to Foo::bar since it doesn't care whether this is an lvalue or an rvalue. bar will return a copy of foo, which will then be copied back into foo.
The same thing would have happened if you hadn't used std::move at all. std::move doesn't actually move anything; it simply converts it into an rvalue which can participate in a move operation.
Your real question is this: for any type T which is moveable, is this legal:
T t;
t = T(std::move(t));
The answer is... maybe. The state of a moved-from object is exactly what the writer of T chooses for it to be. For most standard library types, it will be "valid-but-unspecified". This means that you can use functions that don't require preconditions, that will work regardless of the current state.
Move assignment is usually such a function, so that will generally work. Will it work for all types? There is nothing which guarantees that it will. It depends on how move support is implemented for each type. Some user-defined types can't achieve even a "valid-but-unspecified" state, which means the only thing you're allowed to do with a moved-from object is destroy it.
It all depends on how users choose to work. However, I would say that a type for which t = T(std::move(t)) does not work (that is, yields undefined behavior) would be a very ill-behaved type.
Not super safe, it is sometimes hard to break down, if it's just for a simple program/game it should be fine, I would recommend that you use a more secure code for a big program :)
Overall it's not going to be too bad if it breaks.
What I mean by super safe is that it can't handle a break, if you decide to use a function of a standard way of doing it (e.g: using Struct like you have)
Sorry I can't supply as much information as I can, I'm newish to struct
if anyone wants to edit my answer they can.
Thanks,
~Coolq
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
When I was learning C++, I was told that if you want to have multiple classes reference the same object, you should give both a pointer to the object. In Modern C++, I'd probably interpret this as the object being a unique_ptr and the classes holding non-owning-raw-pointers.
Recently, my mentor at work suggested that you should only use pointers when you plan on having the store point to a different object or null at some point. Instead, you should store references in classes.
Reference member variables are a thing I've actually never seen before, so I was looking for advice on what the concerns were... It makes sense... You're saying that this reference is assumed to never be null... I guess the concern would then be that you couldn't /check/ for null. It would have to be an invariant of your class...
How about how this applies to using the references for polymorphism?
Update:
The answer that I selected covers my questions pretty well, but I thought I'd clarify for future readers. What I was really looking for was an idea of the consequences of using a reference rather than a pointer as a class member. I realise that the way the question was phrased made it sound more like I was looking for opinions on the idea.
Should I store references in classes in c++?
yes, why not. This question is IMO 'primarily opinion-based', so my answer is based on my own experience.
I use member references when I dont need what pointers have to offer, this way I limit possiblity that my class will be wrongly used. This means among other possibility to bind new value, assign nullptr, you cant take pointer to reference, you cannot use reference arithmetics - those features are missing in references. You should also remember that reference is not an object type, this means among others that if you put a reference as struct member, then it is no longer POD - i.e. you cannot use memcpy on it.
You should also remember that for classes which have non static reference member, compiler will not generate implicit constuctors.
For me this means references as variable members are mostly usefull when class is some kind of wrapper, or a holder. Below is an example which also shows an alternative implementation using pointer member type. This alternative implementation gives you no additional benefit to the reference one, and only makes it possible to introduce Undefined Behaviour.
struct auto_set_false {
bool& var;
auto_set_false(bool& v) : var(v) {}
~auto_set_false() { var = false; }
};
struct auto_set_false_ptr {
bool* var;
auto_set_false_ptr(bool* v) : var(v) {}
~auto_set_false_ptr() { *var = false; }
};
int main()
{
// Here auto_set_false looks quite safe, asf instance will always be
// able to safely set nval to false. Its harder (but not imposible) to
// produce code that would cause Undefined Bahaviour.
bool nval = false;
auto_set_false asf(nval);
bool* nval2 = new bool(true);
auto_set_false_ptr asf2(nval2);
// lots of code etc. and somewhere in this code a statement like:
delete nval2;
// UB
}
It is generally not a good idea to store references in a class because the class cannot be default constructed, copy assigned, move assigned, and the member cannot be changed (the reference cannot be rebound).
That renders the class uncopieable. It therefore cannot be copied, moved or placed in most containers. The far more flexible and less surprising solution is to store a pointer or a std::refernce_wrapper.
IMO references works like pointers.
The only difference is in dynamic_cast: a failed cast produces a nullpointer with pointers, and results in a throw an exception with references.
References are far better than pointers because of one reason: you don't have to play with nulls.
A reference can't be null and it is a big value to not have to check for nulls.
The small difficulty is that you have to assign reference member in a constructor.
But you can definitely change it later to other non-null value pointing to an object of a class of the member or subclass of this class.
So it supports inheritance like pointers do.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
Is it possible to have automatically generated destructors in C++?
It is too big burden to do it by ourselves all the time. Is it so hard
for the compiler to generate destructors? Can't it detect what is a "resource"
and release it in the destructor?
Certainly it is, and that's exactly what the language does. If you don't declare a destructor, then one will be generated for you: it will invoke the destructor of each member and base subobject.
You only need to write your own destructor if you're managing a resource that isn't released automatically; for example, a raw pointer to something that you allocated with new. You shouldn't need such a thing in most classes - use containers, smart pointers and other RAII types to manage these automatically for you.
It can't be detected accurately. Even if the compiler observes that you allocate a resource in the constructor or in some other function of the object, it doesn't necessarily follow that it should be released in the destructor. It all depends whether or not the object "owns" the resource.
Fortunately, C++ does provide a means for you to inform the compiler explicitly what resources the object owns. This means is called "smart pointers", and the types you should read about are shared_ptr and unique_ptr. You can avoid writing destructors by thorough use of smart pointers. The compiler generates a destructor that destroys all your data members, so if the data members are smart pointers then the resources they control are destroyed at the appropriate time.
Is it so hard for the compiler to generate destructors?
It is not a question of what is easy or hard for the compiler to do. It is a question of a fundamental tenet of C++ programming:
You shouldn't have to pay for what you don't need.
This philosophy prevails in every aspect of the design of the language, including how destructors are defined and work.
Every class needs a destructor of some kind. That's why the compiler automatically writes one for you if you don't do it yourself. This implicit destructor destroys all members and base classes in a specific order and in a specific way. Sometimes this isn't what you really want, but that compiler can't assume this. A classic case is with smart pointer classes. A smart pointer class will have a raw pointer to the controlled object somewhere, but the compiler doesn't know if that pointer should be deleted -- maybe you are implementing a reference-counter smart pointer. If you need the destructor to actually delete the pointer, that you have to write yourself.
Another case is with deleteing derived classes. Consider:
Base* p = new Derived;
delete p;
If Derived has a bunch of stuff in it which needs to be released, then you need to make sure that when you delete the Derived object through the Base pointer, Derived's destructor is the one that's actually called -- even though the compiler has no way of knowing at the call site that p actually points to a Derived. In order to make this work, you need to make Base::~Base a virtual destructor.
Neither C++ nor C++11 have garbage collection. C++11 does introduce a number of managed pointer classes in the memory header file--shared_ptr, weak_ptr and unique_ptr. These are designed to help prevent memory leaks. For an explanation see C++ Smart Pointers tutorial and Smart Pointers (Modern C++) on MSDN.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can moved objects be used?
What constitutes a valid state for a “moved from” object in C++11?
When implementing move semantics in C++11, should the moved-from object be left in a safe state, or can it be just left in a "junk" state?
e.g. What is the preferred option to implement move constructor in the following example of a C++11 wrapper to a raw FILE* resource?
// C++11 wrapper to raw FILE*
class File
{
FILE* m_fp;
public:
// Option #1
File(File&& other)
: m_fp(other.m_fp)
{
// "other" left in a "junk" state
}
// Option #2
File(File&& other)
: m_fp(other.m_fp)
{
// Avoid dangling reference in "other"
other.m_fp = nullptr;
}
...
};
The only thing you must be able to do with a moved-from object is destroy it. Beyond that, it's up to your class what the normal class invariants are and whether moved-from objects bother to satisfy them.
For example, it's a good idea to ensure that you can assign to the object, just in case someone wants to use std::move on an instance and give it a new value later. [Edit: as pointed out in an answer to one of the suggested-dupe questions, the general std::swap template moves from an object and then move-assigns to it, so if you don't ensure this then either you need to specialize std::swap or you need to forbid users of your class from using it.]
Since your class does nothing in its destructor, either option is fine. Option 2 might be easier for users to work with, but then again if they're coding on the assumption that they can't do anything with a moved-from object, then it makes no difference. Since the class is incomplete, though, that might change when you write the destructor.
An object that has been moved from is still an object, and it must be in a valid state, although it may be indeterminate. In particular, it should be possible to assign a new value to it safely (and of course it must be destructible, as #Steve says). It's up to you what particular semantics you want to give your class, as long as a moved-from object remains valid.
In general, you should think of "move" as an optimized "copy". However, for some classes that are intrinsically "move-only", such as unique_ptr, additional guarantees may be appropriate – for example, unique_ptr promises that after it has been moved-from it is null, and surely nothing else really makes sense.
(Your actual code is incomplete, but given that a FILE* is a sort-of move-only resource, it is probably broken and you should try to emulate unique_ptr as closely as possible – or even use it directly.)