Forum Replies Created
-
AuthorPosts
-
::
Your observation is right. The implementation of clone based on a std::unique_ptr is no covariant return type. It only simulates a covariant return type using an upcast. Smart pointers (std::unique_ptr and std::shared_ptr) do not support the covariant return type. This is a C++ language limitation.
::Thanks to the hidden friend idiom, the compiler finds the swap implementation defined in Foo. If Bar has no optimized swap, std::swap kicks in.
Here is the following example on goldbolt: https://godbolt.org/z/WTKdT4rPK
#include <algorithm> #include <iostream> struct Foo { friend void swap(Foo&, Foo&){ std::cout << "hidden friend\n"; } }; struct Bar{}; template <typename T> void swapMe(T& a, T& b) { std::cout << "callSwap\n"; using std::swap; swap(a, b); } int main() { Foo f, f2; swapMe(f, f2); std::cout << "\n"; Bar b, b2; swapMe(b, b2); }
::I’m don’t know in detail how the strong exception guarantee is provided but here are a few thoughts.
First, the move constructor of T must be noexcept.If an allocation is required due to the push_back call, the follwing steps happen:
- Allocate a buffer
- Construct the value in the new buffer
- Move the values from the old buffer to the new one
This means, that the original vector is only modified in step 3. Step 1 and 2 must have rollback semantics.
::Of course, this example is hypothetical. Suppose, modifyVector throws and catches its exception. Now, you have no idea in which state tmp is. In this case, you don’t care because myVec was not modified.
A try except block does not give you rollback semantics. Assume, modifyVector throws an exception after some side effect such as std::cout. Even when you catch the exception in the main function, the std::cout call cannot be undone.
20. April 2023 at 22:45 in reply to: Required noexcept swap function for Copy-and-Swap idiom #558251::You cannot annotate a function with the “strong exception guarantee”. Therefore, the compiler does not know if the function provides the “strong exception guarantee”. You can only assign noexcept to a function. The expression “strong exception guarantee” is a semantic categorization, but noexcept a syntactic constraint (keyword).
::- My question is if the function run can be executed by more than one thread simultaneously. If so, there is a race condition.
- If run is executed single threaded I have another question. You assume in your function that the following calls are executed in one atomic step. This should be provided because you act on a local msg that provides you transactional semantics.
auto msg = input_.waitDequeueNotification(); if (msg)output_.enqueueNotification(std::move(msg));
::First of all, is the run member function atomic?
void run() { while (is_running_) { calculate(); auto msg = input_.waitDequeueNotification(); if (msg) { output_.enqueueNotification(std::move(msg)); } } }
If not, you have a race condition. It is likely the msg may be consumed more than once. This is pretty similar to my example “Breaking of invariants” here: https://www.modernescpp.com/index.php/malicious-race-conditions.
::Exactly. You have a std::unique_ptr<Sofa> which could own a Sofa. I completed your example and added a default constructor to Sofa. You see that no Sofa is created (https://godbolt.org/z/KG9x9f6Wb):
#include <iostream> #include <memory> class Sofa { public: Sofa() { std::cout << "Sofa created"; } }; class House { public: House(): sofa2(nullptr) {} private: std::unique_ptr<Sofa> sofa; Sofa* sofa2; }; int main() { House h; }
In this case, it would be fine if the constructor of Sofa is private.
::Excellent point. You should prefer smart pointers to raw pointers. This rule still applies for design patterns. I start in my design pattern mentoring classically. Consequentially, my implementations are pointer based. In upcoming weeks, I modernize these patterns and introduce idioms.
By the way, most of the time I use a pointer to a local and have, therefore, no memory issues.
Here is the composite based on std::shared_ptr.
#include <iostream> #include <memory> #include <string> #include <vector> class Graphic { public: virtual void print() const = 0; virtual ~Graphic() {} }; class GraphicComposite : public Graphic { std::vector<std::shared_ptr<const Graphic>> children; const std::string& name; public: explicit GraphicComposite(const std::string& n): name(n){} void print() const override { std::cout << name << " "; for (auto c: children) c->print(); } void add(std::shared_ptr<const Graphic> component) { children.push_back(component); } void remove(std::shared_ptr<const Graphic> component) { std::erase(children, component); } }; class Ellipse: public Graphic { private: const std::string& name; public: explicit Ellipse(const std::string& n): name (n) {} void print() const override { std::cout << name << " "; } }; int main(){ std::cout << '\n'; const std::string el1 = "ellipse1"; const std::string el2 = "ellipse2"; const std::string el3 = "ellipse3"; const std::string el4 = "ellipse4"; auto ellipse1 = std::make_shared<Ellipse>(el1); auto ellipse2 = std::make_shared<Ellipse>(el2); auto ellipse3 = std::make_shared<Ellipse>(el3); auto ellipse4 = std::make_shared<Ellipse>(el4); const std::string graph1 = "graphic1"; const std::string graph2 = "graphic2"; const std::string graph3 = "graphic3"; auto graphic1 = std::make_shared<GraphicComposite>(graph1); auto graphic2 = std::make_shared<GraphicComposite>(graph2); auto graphic = std::make_shared<GraphicComposite>(graph3); graphic1->add(ellipse1); graphic1->add(ellipse2); graphic1->add(ellipse3); graphic2->add(ellipse4); graphic->add(graphic1); graphic->add(graphic2); graphic1->print(); std::cout << '\n'; graphic2->print(); std::cout << '\n'; graphic->print(); std::cout << '\n'; graphic->remove(graphic1); graphic->print(); std::cout << "\n\n"; }
Now, I can optimize the implementation and share nodes. This is a very nice use case of std::shared_ptr.
In the following implementation (https://godbolt.org/z/73zroqfKx), I have only one ellipse and I remove it from graphic2. Observe the use count of ellipse.
#include <iostream> #include <memory> #include <string> #include <vector> class Graphic { public: virtual void print() const = 0; virtual ~Graphic() {} }; class GraphicComposite : public Graphic { std::vector<std::shared_ptr<const Graphic>> children; const std::string& name; public: explicit GraphicComposite(const std::string& n): name(n){} void print() const override { std::cout << name << " "; for (auto c: children) c->print(); } void add(std::shared_ptr<const Graphic> component) { children.push_back(component); } void remove(std::shared_ptr<const Graphic> component) { std::erase(children, component); } }; class Ellipse: public Graphic { private: const std::string& name; public: explicit Ellipse(const std::string& n): name (n) {} void print() const override { std::cout << name << " "; } }; int main(){ std::cout << '\n'; const std::string el = "ellipse"; auto ellipse = std::make_shared<Ellipse>(el); const std::string graph1 = "graphic1"; const std::string graph2 = "graphic2"; const std::string graph3 = "graphic3"; auto graphic1 = std::make_shared<GraphicComposite>(graph1); auto graphic2 = std::make_shared<GraphicComposite>(graph2); auto graphic = std::make_shared<GraphicComposite>(graph3); graphic1->add(ellipse); graphic1->add(ellipse); graphic1->add(ellipse); graphic2->add(ellipse); graphic->add(graphic1); graphic->add(graphic1); graphic1->print(); std::cout << '\n'; graphic2->print(); std::cout << '\n'; graphic->print(); std::cout << '\n'; std::cout << "ellipse.use_count(): " << ellipse.use_count() << '\n'; graphic2->remove(ellipse); graphic->print(); std::cout << '\n'; std::cout << "ellipse.use_count(): " << ellipse.use_count() << '\n'; std::cout << "\n\n"; }
-
AuthorPosts
