Forum Replies Created
-
AuthorPosts
-
::
I already added https://www.cppstories.com/ to the blogs. I will extend the resources page with a newsletter list.
::I used const&, but I may change it because there is one significant advantage of copying and moving it: no sharing. Meaning, when you use your class in a concurrent environment and take it by const&, you have to protect it.
To put it the other way around. I will only use a const& if I’m 100% sure my code will never run in a concurrent environment. In this regard, the rules F.15 (proposes const&) and CP.1 (CP.1: Assume that your code will run as part of a multi-threaded program) contradict.
::Your question is too general. I only can answer them in a generic way.
My first impression is that a too complex constructor is a code smell. The Core Guidelines provide a few rules about constructors:
- C.41: A constructor should create a fully initialized object
- C.42: If a constructor cannot construct a valid object, throw an exception
- C.44: Prefer default constructors to be simple and non-throwing
- C.45: Don’t define a default constructor that only initializes data members; use member initializers instead
- C.49: Prefer initialization to assignment in constructors
- C.51: Use delegating constructors to represent common actions for all constructors of a class
These rules should give the first idea and help simplify the complex construction, but I have an additional remark missing in the core guidelines.
- Maybe, your class has too many responsibilities. Create additional class or inner classes. This makes your initial constructor leaner because you delegate construction to other classes.
- Similarly to C.51, use delegating constructors if possible or, as you mentioned, delegate the construction job to a factory.
::You need a virtual destructor when you want to have virtual destruction behavior. This means that you destruct a derived object using the interface of the base class. A virtual default destructor cannot be pure, because it is always implicit called, when you destruct a derived object through an abstract base class.
27. July 2023 at 18:30 in reply to: Definition of static member outside the class in Singleton Pattern #629239::Defining a static outside a class was a limitation of the C++ standard. Using static int a; was only a declaration. This limitation is gone with C++17. No, you can define a static inside a class: inline static int a{};
class Test { static int a; // declaration inline static int b; // definition };
::Your first concern should not be performance but readability. The main difference is that the switch statement is a compile-time decision. You have to code it. On the contrary, a std::map is a run-time decision because you can add at run time additional cases. From the performance point of view, you should use instead of the std::map a std::unordered_map. A std::unordered_map provides constant access time but a std::map only logarithmic access time.
I discussed all options in two of my previous posts:
::This is probably a side effect of the two phase lookup of templates (two-phase_lookup). This is important, when you have dependent names (Dependent Names). What is a dependent name? A dependent name is essentially a name that depends on a template parameter.
This is the essential difference:
- Non-dependent names are looked up at the point of the template definition.
- Dependent names are looked up at the point of the template instantiation.
Your first example exampleWithExtraTemplateParam uses a dependent name.
Your second example exampleWithoutExtraTemplateParam uses a non-dependent name.
At the point of template definition, the compiler assumes that both functions return a boolean. It considers both function in isolation. This is why the second example fails. This is a redeclaration of a function template.
typename std::enable_if_t<std::is_same_v<T, bar>, bool> check(){ return true; } typename std::enable_if_t<!std::is_same_v<T, bar>, bool> check(){ return false; }
On the contrary, at the point of template instantiation, the compiler knows that only one version is created.
From the compiler definition, the compiler regards your program as:
#include <iostream> #include <type_traits> class foo; class bar; template<class T> struct is_bar { bool check(){ return true; } bool check(){ return false; } }; int main() { is_bar<foo> foo_is_bar; is_bar<bar> bar_is_bar; if (!foo_is_bar.check() && bar_is_bar.check()) std::cout << "It works!" << std::endl; }
This gives you the same error message.
::You need lazy evaluation. For example, short circuit evaluation helps here. Boolean operators in C++ apply it. Short circuit evaluation means, essential, that the evaluation of a boolean expression stops if the result is already given.
Here is the program having the same workflow as yours.
#include <iostream> constexpr int foo(int v1, int v2) { constexpr int x = 9; constexpr int y = 9; if constexpr (!std::is_same<decltype(v1), int>::value) { static_assert(std::is_same<decltype(v1), int>::value || (v1 != v2)); return 1; } return 2; } int main() { constexpr int f = foo(1, 1); static_assert(f == 2); }
I used the static_assert inside the constexpr if, but you can also use it as a standalone feature: static_assert(std::is_same<decltype(v1), int>::value || (1/0));
Another way to achieve this would be to put your static_assert inside the member function of a class template. They are only created if used. I wrote more about it here: Surprise Included: Inheritance and Member Functions of Class Templates
::In the case of a consteval function, just use an if instead of a static_assert.
#include <iostream> #include <string> consteval auto doubleMe( int val1, int val2 ) noexcept { constexpr int a = 10; constexpr int b = 10; static_assert( a == b, "OK"); if (val1 == val2) // do something at compile time; for example std::expected return 2 * val1; } int main() { auto res = doubleMe(1010, 1010); ++res; }
::The delimiter specifies the content of the raw string.
- Default delimiter:
- Begin: R”(
- End: )”
- Special delimiter:
- Begin: R”TRENNER(
- End: )TRENNER”
TRENNER can be “almost” any sequence of characters. The delimiter allows you to use )” inside the raw string which would by default end the raw string.
::Raw Strings support an extended syntax. TRENNER can be 16 characters long. Now, you can use a string termination symbol ()”) inside a raw string. Consider raw3 in the following program:
#include <iostream> #include <string> int main(){ std::cout << '\n'; std::string nat = "C:\temp\newFile.txt"; std::cout << nat << '\n'; // including \t \n std::string raw1 = std::string(R"(C:\temp\newFile.txt)"); std::cout << "\n" << raw1 << '\n'; // including \t \n and using a delimiter std::string raw2 = std::string(R"TRENNER(C:\temp\newFi)"le.txt)TRENNER"); std::cout << "\n" << raw2 << '\n'; std::cout << '\n'; }
::I don’t know if I get your question.
In general, you should only use friend if necessary, because friend breaks encapsulation.
Friendship cannot be inherited and is not transitive.
- If a class Base grants friendship to a class Derived, a from class Base derived class Derived is not automatically a friend of Base.
- If class B is a friend of class A and Class C is a friend of class B, class C is not automatically a friend of class A.
Friendship is pretty special for templates. You can read it here: The Special Friendship of Templates
10. July 2023 at 11:48 in reply to: curiosity: “you can skip some values between enumerations” #629097::You are right, but this is regarded as a code small. See the C++ Core Guidelines: Enum.8: Specify enumerator values only when necessary
-
AuthorPosts
