Forum Replies Created
-
AuthorPosts
-
::
The idea of the Decorator is to dynamically extends an object with responsibilities.
Have a look at your slightly modified example on godbolt:
#include <string> #include <iostream> template<typename Printable> struct RepeatPrint : Printable { explicit RepeatPrint(Printable const& printable) : Printable(printable) {} void repeat(unsigned int n) const { while (n-- > 0) { this->print(); } } }; struct Person { std::string first; std::string last; void print() const { std::cout << first << "," << last << "\n"; } }; int main() { Person p{"Diana", "O"}; RepeatPrint<Person> myRepeat(p); myRepeat.repeat(4); std::cout << "---\n"; myRepeat.print(); }
The class RepeatPrint extends the interface of Person.
Person support the print call, but RepeatPrint supports the print and the repeat call.
This means that RepeatPrint extends the interface of Print. This extensions is not dynamically but statically (compile time).
print is a so-called non-dependent name because its name does not depend on the template parameter T. Non-dependent names are looked up and bound at the point of the template definition. At the point of the template definition there is no print function available. You can overcome this issue in three ways:
- Make the name dependent: this->print()
- Introduce the name into the current scope: using Printable::print
- Call the name fully qualified: Printable::print()
This post gives you more information: Surprise Included: Inheritance and Member Functions of Class Templates
::To make your question more genernal:
std::tie creates lvalue referenes.
std::tuple creates a std::tuple (std::pair creates a std::pair. A std::pair is a two elements tuple).
std::tie is often used as a convenience function to unpack values. I use std::pair, std::tuple, and std::tie in the following example.
#include <functional> #include <iostream> #include <tuple> std::tuple<int, double, char> returnTuple() { return std::make_tuple(2000, 10.5, 'a'); } int main(){ // make a tuple auto tup1= std::make_tuple(1, 2, 3); // print the values std::cout << "std::tuple tup1: ("<< std::get<0>(tup1) << ", " << std::get<1>(tup1) << ", " << std::get<2>(tup1) << ")" << '\n'; std::cout << '\n'; int first = 1; int second = 2; int third = 3; int fourth = 4; // create a tuple with references auto tup2= std::make_tuple(std::cref(first), std::ref(second), std::ref(third), fourth); // print the values std::cout << "std::tuple tup2: (" << std::get<0>(tup2) << ", " << std::get<1>(tup2) << ", " << std::get<2>(tup2) << ", " << std::get<3>(tup2) << ")" << '\n'; std::cout << '\n'; //change the values // std::get<0>(tup2)= 1001; ERROR because of std::cref(first) first = 1001; std::get<1>(tup2) = 1002; third = 1003; fourth = 1004; // print the values std::cout << "std::tuple tup2: (" << std::get<0>(tup2) << ", " << std::get<1>(tup2) << ", " << std::get<2>(tup2) << ", " << std::get<3>(tup2) << ")" << '\n'; std::cout << " variables: " << first << " " << second << " " << third << " " << fourth << '\n'; std::cout << '\n'; first = 1; second = 2; third = 3; fourth = 4; int a; int b; // bind the 2th and 4th argument to a and b std::tie(std::ignore, a, std::ignore, b)= tup2; // print the values std::cout << "a: " << a << '\n'; std::cout << "b: " << b << '\n'; std::cout << '\n'; // will also work for std::pair std::tie(a, b)= std::make_pair(3001, 3002); // print the values std::cout << "a: " << a << '\n'; std::cout << "b: " << b << '\n'; int res1; double res2; char res3; std::tie(res1, res2, res3) = returnTuple(); std::cout << '\n'; // print the values std::cout << "res1: " << res1 << '\n'; std::cout << "res2: " << res2 << '\n'; std::cout << "res3: " << res3 << '\n'; }
5. June 2023 at 20:22 in reply to: Video page about policyComposition.cpp shows templatesPolicy.cpp instead #628693::So far, I never saw this syntax. Clang give an error message using the following code (https://godbolt.org/z/s15WM3TY6):
enum myEnum{}; enum class myEnumClass{}; class myClass{}; void myFunction(enum myEnum a_myEnum); // (1) void myFunction(enum class myEnumClass a_myEnumClass); // (2) void myFunction(class myClass a_myClass); int main() { static_cast<enum myEnum>(0); static_cast<enum class myEnumClass>(0); }
(1) and (2) are references to enumerations, but only (1) is valid. The same holds for the use of enum in the main function.
::I asked Alf P. Steinbach, moderator of a C++ group: Why do smart pointers not support the covariant return type?
Here is his answer: “Well, because C++ functions return by value, so an override must return something of the same physical size. And raw pointers and references to class type objects are all the same size.” On the contrary, std::std::unique_ptr<Base> and a std::unique_ptr<Derived> must not have the same physical size.
11. May 2023 at 19:08 in reply to: Video about visitVariantsOverloadPattern.cpp shows visitVariants.cpp #606565::You mentioned the use-cases I have in mind: static polymorphism and mixin classes. Fedor Pikus is a big fan of CRTP. When you want to see what’s possible, read his book “Hands-On Design Patterns with C++“.
::I regards type erasure not as static polymorphis because the is a virtual call involved. When you want to have static polymorphism, you have only generic programming. Essentially, you can do it in two ways:
- Use templates, best combined with concept in C++20.
- Use CRTP: With deducing this in C++23, this is not curious anymore. I will add a example in the next days.
::This is strange. I should have the same configuration: https://godbolt.org/z/a6vP1zvcr. I played with in on my local PC.
::- I see no issue with the example. The function cloneMe is the new owner of interface and creates a clone of it. After the cloneMe call in the main function, the interface is new initialized. Of course, instead of taking the std::unique_ptr by value, cloneMe could take it by const reference. In this case, cloneMe would not own the interface, but only borrow it.
- You are right. I will add a virtual destructor to the solution. The program has undefined behavior.
::Your following example (https://godbolt.org/z/je5nnne6s) should show the differences. Derived uses a covariant return type, but Derived2 not. To use the return type from Derived2’s clone functions as type Derived2 and not Base, you have to downcast it with a dynamic_cast explicitly.
struct Base { virtual Base* clone() const{ return new Base(*this); } virtual ~Base() = default; }; struct Derived : Base { Derived* clone() const override { return new Derived(*this); } void test() {}; }; struct Derived2 : Base { Base* clone() const override{ return new Derived2(*this); } void test() {}; }; int main() { Derived der; Derived* d1 = der.clone(); d1->test(); Derived2 der2; // Derived* d2 = der2.clone(); ERROR Base* b2 = der2.clone(); Derived2* d2 = dynamic_cast<Derived2*>(b2); d2->test(); }
-
AuthorPosts
