Forum Replies Created
-
AuthorPosts
-
::
std::bad_alloc requires the header <new>, but <utility> is not required.
> I commented them out and the code still works.
This is the wrong argumentation. You should always include all required headers. For example, the following program works with msvc 19.37, but fails with msvc 19.14: https://godbolt.org/z/KGKP5ao1h.
#include <iostream> // #include <string> int main() { std::string s = "Works on GCC, but not ot MSVC\n"; std::cout << s; }
The reason is that the old MSVC didn’t include the header <string> in the header <iostream>. I often had the issue in my past with <string> and <vector>, when I compiled a program developed with GCC using MSVC.
::I cannot give you a definite answer. I will ask Daniela Engert after the CppCon and will update this answer.
Currently, you deploy the named module (*.ixx on Windows) and build the binaries on your target platform. As an optimization step, it is possible to distribute the binary module interface file directly.
C++23 supports a modularized standard library. To use this C++23 feature, you have to build the binary module interface. The following tutorial “Import the C++ standard library using modules from the command line” describes all steps. You need at least a Visual Studio 2022 preview version 17.5.
I found the files modules.json, std.compat.ixx, and std.ixx here (local machine): C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.37.32822\modules
Here are the steps to build and use the modularized standard library in C++23:
- Compile the module std.ixx in an empty directory.
- Import the module.
- Link the executable against the compiled module std.obj.
Alternatively, you can also perform these three steps in one and build your executable that refers to the std.ixx file.
Additionally, the proposal C2473 describes in which direction it may go.
::Let me directly answer your points. Here are you first three ways:
class Test { public: Test(): a2{2} { a1 = 1; } int a1; int a2; int a3{3}; };
- This is not an initialization. This is an assignment. The call a1 = 1 overwrites the default-initialized a1. You can observe this overwrite step when you make a1 constant: https://godbolt.org/z/nKPEnod93
- This is an initialization. Instead of curly braces, you can also use parenthesis: a2(2). Parenthesis have one big disadvantage. They will not detect narrowing conversion: a2(5.5).
- a3{3} is the preferred way. Initialize the data member in the class body. Here are the two corresponding rule from the C++ Core Guidelines:
The rules are special if your class or struct is an aggregate initialization.
struct Test { int a3{5}; }; int main() { Test test{6}; }
You can either initialize it inside the class body (a3), or apply aggregate initialization (test). I strongly prefer the initialization inside the class body. Only if you want to change the default value of a3, you should apply aggregate initialization.
In C++20, you can use in aggregate initialization the name of the member. This is a special form of aggregate initialization and called designated initialization:
struct Test { int a3{5}; }; int main() { Test test{.a3 = 6}; }
::I already discussed the difference between Strategy Pattern and Policy (policy based design) with Klaus Iglberger in NDC TechTown.
Strategy and Policy are essentially the same: You define a family of algorithms and encapsulate them in objects. Klaus uses them as synonyms. I differentiate them: when you do it a run time, I call it Strategy; at compile time, I call it Policy. By the way, you can also call both Dependency Injection.
Template Method and Strategy have similar use cases: You want to have variations of algorithms. Here is the critical difference. You achieve this with the Template Method using subclasses but with the Strategy Pattern by delegation. Template Method is a compile-time mechanism, but Strategy Pattern is a run- time mechanism. Consequentially, the Template Method is a class pattern, but the Strategy Pattern is an object pattern.
::Now, i have it. Sorry, for misunderstanding your question. You can do it without having objects. Here is the simplified and better program:
#include <concepts> #include <iostream> struct Fir { int count() const { return 2020; } }; struct Sec { int size() const { return 2021; } }; int main() { std::cout << '\n'; static_assert(requires(Fir fir){ { fir.count() } -> std::convertible_to<int>; }); static_assert(requires(Sec sec){ { sec.count() } -> std::convertible_to<int>; }); static_assert(requires(int third){ { third.count() } -> std::convertible_to<int>; }); std::cout << '\n'; }
::- Almost, you can define your type-trait isSemiRegular in C++17 and use it:static_assert(isSemiRegular<T>::value). This type-trait is based on std::is_swappable that was added with C++17. Do have the same functionality such as concepts in C++17, you can simulate them with SFINAE (More and More Safe).
- I always prefer a standard implementation if possible. I, therefore, would use std::semiregular. There are many reasons:
- How can you be sure that mine is correct? You have to test it.
- Your coworker can look up the definition in the standard.
- std::semiregular is composed of more elementary concepts. Mine is just a composition of type-traits.
- You may not be allowed to use my concept, but only a predefined, standardized concept.
::I don’t see another way. static_assert requires a compile-time predicate. Requires expressions are compile-time predicates. Here are two variations. The first one uses a temporary Fir. The second one doesn’t instantiate Fir. The expression std::declval<Fir>().count() means. Give me the type I would get if I instantiate Fir and invoke count() on it (https://godbolt.org/z/4bnvdrq1o).
#include <concepts> struct Fir { int count() const { return 2020; } }; int main() { static_assert(requires { { Fir().count() } -> std::convertible_to<int>; }); static_assert(requires { { std::declval<Fir>().count() } -> std::convertible_to<int>; }); }
::Honestly, your example is both, strange and interesting:
First, you need the header compare and second, your default created three-way comparison operator on Man implicitly wants to generate the three-way comparison operator on Relational. The three-way comparison operator for Man is explicitly deleted because the compiler cannot generate the three-way comparison operator for Relational.
You can solve this issue in two ways:
- Default the three-way comparison operator on Relational and delete its six relational operators: https://godbolt.org/z/j6xYY1esr
#include <compare> #include <iostream> #include <string> // Relational Operators template <typename Derived> struct Relational { auto operator <=> (const Relational& m) const = default; }; // Man class Man: public Relational<Man>{ public: explicit Man(const std::string& n): name{n}{} auto operator <=> (const Man& m) const = default; private: std::string name; }; int main(){ std::cout << std::boolalpha << '\n'; Man man1{"grimm"}; Man man2{"jaud"}; std::cout << "man1 < man2: " << (man1 < man2) << '\n'; std::cout << "man1 > man2: " << (man1 > man2) << '\n'; std::cout << "man1 == man2: " << (man1 == man2) << '\n'; std::cout << "man1 != man2: " << (man1 != man2) << '\n'; std::cout << "man1 <= man2: " << (man1 <= man2) << '\n'; std::cout << "man1 >= man2: " << (man1 >= man2) << '\n'; std::cout << '\n'; }
- Define the three-way comparison operator on Man: https://godbolt.org/z/Pso56E55h
#include <compare> #include <iostream> #include <string> // Relational Operators template <typename Derived> struct Relational { friend bool operator > (Derived const& op1, Derived const& op2){ return op2 < op1; } friend bool operator == (Derived const& op1, Derived const& op2){ return !(op1 < op2) && !(op2 < op1); } friend bool operator != (Derived const& op1, Derived const& op2){ return (op1 < op2) || (op2 < op1); } friend bool operator <= (Derived const& op1, Derived const& op2){ return (op1 < op2) || (op1 == op2); } friend bool operator >= (Derived const& op1, Derived const& op2){ return (op1 > op2) || (op1 == op2); } }; // Man class Man: public Relational<Man>{ public: explicit Man(const std::string& n): name{n}{} auto operator <=> (const Man& m) const { return name <=> m.name; } private: std::string name; }; int main(){ std::cout << std::boolalpha << '\n'; Man man1{"grimm"}; Man man2{"jaud"}; std::cout << "man1 < man2: " << (man1 < man2) << '\n'; std::cout << "man1 > man2: " << (man1 > man2) << '\n'; std::cout << "man1 == man2: " << (man1 == man2) << '\n'; std::cout << "man1 != man2: " << (man1 != man2) << '\n'; std::cout << "man1 <= man2: " << (man1 <= man2) << '\n'; std::cout << "man1 >= man2: " << (man1 >= man2) << '\n'; std::cout << '\n'; }
In both cases, there is no need to have a class Relational and use CRTP. Here is the straightforward solution:
#include <compare> #include <iostream> #include <string> // Man class Man { public: explicit Man(const std::string& n): name{n}{} auto operator <=> (const Man& m) const = default; private: std::string name; }; int main(){ std::cout << std::boolalpha << '\n'; Man man1{"grimm"}; Man man2{"jaud"}; std::cout << "man1 < man2: " << (man1 < man2) << '\n'; std::cout << "man1 > man2: " << (man1 > man2) << '\n'; std::cout << "man1 == man2: " << (man1 == man2) << '\n'; std::cout << "man1 != man2: " << (man1 != man2) << '\n'; std::cout << "man1 <= man2: " << (man1 <= man2) << '\n'; std::cout << "man1 >= man2: " << (man1 >= man2) << '\n'; std::cout << '\n'; }
1. September 2023 at 09:41 in reply to: virtual destructor and “missing” delete (for raw pointers) #629685::This program has a three memory leaks (messPoin1, messPoin2, and messPoin3) for two reasons (https://godbolt.org/z/df5G9EE9W):
- The destructor is not called.
- The destructor is not virtual.
::First of all: Very good point.
Honestly, I don’t put too much design thoughts into this concept example. My primary intention is to show you a use case for a concept applied to a member function.
Additionally, you are right. The member function is not a template, but the class. I have to correct this. Here is a generic member function. It has an additional template parameter T2.
template <typename T> struct MyVector{ template <typename T2> void push_back(const T2&) requires std::copyable<T2> {} };
I don’t know if your following example makes more sense or not:
template <typename T> requires std::copyable<T> struct MyVector{ void push_back(const T&) {} };
To answer this question, we have too less context. It is pretty common, that member functions of a class have more constraints than others. Some of them can be const, some of them can be thread safe, or, such as in my original case, have additional constraints.
template <typename T> requires std::copyable<T> struct MyVector{ void push_back(const T&) {} std::size_t getSize() const { // ... } // ... };
In this concrete example, I regard the concept std::copyable as not appropriate for my member function getSize().
25. August 2023 at 11:31 in reply to: Constraint on template parameters vs compile time error #62955525. August 2023 at 11:13 in reply to: Constraint on template parameters vs compile time error #62955224. August 2023 at 23:03 in reply to: Constraint on template parameters vs compile time error #629547::This example is tricky: https://godbolt.org/z/eaeE6KMa5.
The compiler tries to instantiate your first two overloads, but the instantiations fail due to SFINAE (Substitution Failure Is Not An Error). SFINAE means that the compiler ignores are failing template instantiation and removes it from the set of all overloads. Now, ADL (Argument Dependent Lookup) kicks in. Because your overloads use members of the std namespace, the compiler considers the std namespace also for resolving advance. Consequentially, std::advance kicks in and is used.
-
AuthorPosts
