Forum Replies Created
-
AuthorPosts
-
24. August 2023 at 19:34 in reply to: coroutines as an elegant solution in lambda improvements #629545::
Now, I have it. This is what I meant:
#include <ranges> #include <deque> #include <string> #include <vector> auto constrainedTemplateParameter = []<std::ranges::contiguous_range Cont>(Cont cont) { return cont.size(); }; auto abbreviatedFunctionTemplate = [](std::ranges::contiguous_range auto cont) { return cont.size(); }; int main() { std::vector myVec{1, 2, 3}; std::string myStr= "dasfasdf"; std::deque myDec{1, 2, 3}; constrainedTemplateParameter(myVec); constrainedTemplateParameter(myStr); // constrainedTemplateParameter(myDec); ERROR abbreviatedFunctionTemplate(myVec); abbreviatedFunctionTemplate(myStr); // abbreviatedFunctionTemplate(myDec); ERROR }
Here is the example: https://godbolt.org/z/7cGGxWzd3
::Based on Driton’s example, here is the simplified one:
#include <iostream> template <typename Derived> concept DerivedLike = requires(Derived d) { d.interface(); }; struct Base_Test { virtual void interface() { std::cout << "Base Test Implementation\n"; } }; struct Derived_Test: Base_Test { void interface() override { std::cout << "Derived Test Implementation\n"; } }; struct Derived_Test2: Base_Test {}; template <DerivedLike Derived> void execute(Derived& d){ d.interface(); } int main(){ Base_Test b1; Derived_Test db1; Derived_Test2 db2; std::cout << "\nCalling Test\n\n"; execute(b1); execute(db1); execute(db2); }
You can try it out on Compiler Explorer: https://godbolt.org/z/qTEYscvhG.
::You are right, but you will get a template instantiation error. This means your compile-time error may be very hard to read. If you don’t believe me, try to invoke on an old gcc std::sort on a std::list. You may get more than 100 error lines. Essentially, template instantiation is duck typing at compile time.
On the contrary, when you use concepts, you will get a well-defined error message.
::Q1. You can always choose the most conservative approach and use an exclusive lock like std::mutex under the hood. This is fine, but has a serious performance penalty. By the way, this is the issue the C implementation of Python has with the GIL. Python uses a Global Interpreter Lock (GIL) when the underlying C functionality is called. They want to get rid of the GIL since the beginning of Python. C++ has a meta rule: you don’t pay for what you don’t use. This called the zero-overhead principle. Or as the C++ Core Guidelines state: P.9: Don’t waste time or space. Therefore, using this conservative approach is not option in C++.
Q2: The point is that you develop a library that is used in different environment. Consequentially, you have to tailor your library to its use case. Here is the next rule from the C++ Core Guidelines: CP.1: Assume that your code will run as part of a multi-threaded program.
23. August 2023 at 17:06 in reply to: coroutines as an elegant solution in lambda improvements #629489::I use Money as a key inside std::unordered_set<Money>. Value objects like Money are typically used for keys in associative containers.
For that, Money must be hashable and support equality.
#include <iostream> #include <unordered_set> class Money { public: Money() = default; Money(double a): amt(a) {} bool operator == (const Money& ) const = default; double getAmount() const { return amt; } private: const double amt{0.0}; }; template<> struct std::hash<Money> { std::size_t operator()(Money const& mon) const noexcept { return std::hash<double>{}(mon.getAmount()); } }; int main() { std::cout << std::boolalpha << '\n'; Money money1(0.0); Money money2; std::cout << "money1 == money2: " << (money1 == money2) << '\n'; std::unordered_set<Money> myMoneySet{money1 , money2}; for(auto mon: myMoneySet) std::cout << "mon.getAmount(): " << mon.getAmount() << '\n'; std::cout << '\n'; }
When I remove the full specialization of std::hash, the program breaks: https://godbolt.org/z/1r9vrKEoh.
::The combination of the Abstraction and the Implementor is the bridge. They connect between the refined abstraction and the concrete implementation. The concrete implementor is injected into the refined abstraction. The key idea of the bridge is that you can exchange the implementation but also the abstraction.
Typically, you only want to replace the implementation, but not the abstraction. In this case, you don’t need a refined abstraction.
11. August 2023 at 08:11 in reply to: alternative solution of swap exercise? or wrong approach? #629304::Your solution has a few serious issues: https://godbolt.org/z/cn83q8Ehs
#include <string> #include <vector> #include <iostream> class Foo { public: Foo(std::string ss, std::vector<int> vv, int ii) : s(std::move(ss)), v(std::move(vv)), i(std::move(ii)){} Foo swap(const Foo& foo) noexcept { this -> s = foo.s; this -> v = foo.v; this -> i = foo.i; return *this; } std::string getString(){ return this -> s; } private: std::string s{}; std::vector<int> v{}; int i{}; }; int main(){ Foo myFoo(std::string("Hola"), std::vector<int>(3,5), 13); std::cout << myFoo.getString() << std::endl; // Hola Foo myOtherFoo(std::string("Chau"), std::vector<int>(2,4), 17); std::cout << myOtherFoo.getString() << std::endl; // Chau std::cout << std::endl; myFoo.swap(myOtherFoo); std::cout << myFoo.getString() << std::endl; // Chau std::cout << myOtherFoo.getString() << std::endl; // Chau }
- Your swap member function assigns yourself the value of the other foo. In general, this is a memory leak because you would have first to delete yourself before you get a new value.
- Both values are not swapped. The other foo is still in the same state.
::vec.reserve(num): At least, space for num elements of the vector is allocated. You address the capacity of the vector.
vec.resize(num): The vector gets num additional elements, all of them are initialized. You address the size of the vector.
Use vec.reserve(num) if you need a big vector, and you want to avoid permanent reallocation. This is an expensive operation.
The difference between the [] operator and the at operator is that at checks boundaries. at if safe but slower.
11. August 2023 at 07:53 in reply to: std::bind_front and forced exception from the inderlying callable #629302::Dividing by 0 is not catchable. It’s such a deep exception. Here is how I typically deal with dividing by 0: https://godbolt.org/z/jGfzeecsh.
::You use static, when you want to have state or behavior on the class level.
Typical use-cases are the singleton or the borg idiom.
There is also an optimization potential. If a member function can not be inlined, the implicit this pointer must be passed around in an extra register. Thanks to a static member, you can spare the this pointer.
In C++23, the subscript and call operator can be static. I write about it in my post C++23: More Small Pearls.
-
AuthorPosts
