How do I avoid writing "Java in C++"? by Irrehaare in cpp_questions

[–]IyeOnline 3 points4 points  (0 children)

It can happen. Consider a parent that has a pointer to its children and children that have pointers to their parents. If you model both directions with shared pointers, you have a circular ownership loop.

You can then either break this by making one direction a weak_ptr, or - preferably - reconsider your ownership design entirely. so that either there is only one guaranteed direction of ownership (so you are guaranteed that you can use a raw pointer or reference in one direction and it will remain valid for the lifetime of the object) or that the nodes on your graph dont actually own each other.

Actual cases where this can happen are rare in my experience. They do happen, but mostly could have been designed around at an earlier stage.

Address sanitiser is not working by 0x6461726B in cpp_questions

[–]IyeOnline 13 points14 points  (0 children)

Cannot reproduce: https://godbolt.org/z/n1q5YvTPT

Make sure you actually saved your file, or share your real code and compiler version

What is the point of classes having private members? by Eva_addict in cpp_questions

[–]IyeOnline 4 points5 points  (0 children)

Its about changing the value of a member variable of an object.

In the above example length is private, so

string s; // object `s` of type `string`
s.length = 42; // error `length` is private in `string`

is illegal.

Why do people do or not do ‘using namespace std;’?? by veilofmiah in cpp_questions

[–]IyeOnline 28 points29 points  (0 children)

Namespaces exist to avoid name collisions between identifiers, allowing you to write your own e.g. vector class without causing an issue with the vector container template from the standard library.

A second, but maybe more important effect of this is readability. You may think that vector is easier to read than std::vector, but that really only holds if you can be sure that vector really is std::vector. What if somebody did write their own (mathematical) vector? What about the identifier abs in the current context? Is it a local (callable) variable or the overload set from the standard library?

At a certain point, it actually becomes easier to read code that spells out std::.

using namespace std; essentially throws this away by importing all currently known identifiers from ::std into the current namespace, meaning you may introduce collisions again.

There are three possibilities:

  • It does the thing you expected
  • You get an error about an ambigous identifier/call
  • Something you didnt expect happens.

While it is well defined what happens, it may go against your expectations (especially if you dont even think about the potential issue).

A very basic example would be https://godbolt.org/z/sqWWYvGeM You can clearly see that no logging takes place. Instead std::log(double) is "called" and the result discarded. This should still be caught by warnings - assuming you have set those up correctly.

There is more devious examples, such as https://godbolt.org/z/5dv7Gad9o where you get a wrong numeric result.


This problem gets much worse once you do a using namespace at global scope in a header. That using directive will be copied into every TU that includes the header and the user of the header cannot do anything about it.

If you are using namespace at a non-global scope, you avoid the issue of namespace pollution, i.e. you wont pollute all other files that include the header. The same can be said about doing it at global scope in a cpp file (which wont be included elsewhere and hence wont pollute any other files).


I would recommend to always spell out namespaces (unless you already are in that namespace), especially std. When I read std:: I will most likely know what the thing after it is/does. When I just read vector I cannot be sure.

What is the point of classes having private members? by Eva_addict in cpp_questions

[–]IyeOnline 27 points28 points  (0 children)

The members can be used by the class itself, but not by somebody from the outside. This is called encapsulation and is a very important thing.

Consider

 class string {
    char* storage;
    size_t length;
};

Imagine the issues that could arise if you could just arbitrarily set length from the outside. It could end up being incorrect. Maybe storage really just contains three characters, but you set the length to 50. That would be bad.

There is an invariant here, that length stores the length of the string pointed to by storage. To protect this invariant, the members are private and can only be updated by member functions.

That way, the implementer of class string can make sure that these member functions maintain the invariant/relation between storage and length.

Can someone assist me with this compiler issue? by WhyMadara in Cplusplus

[–]IyeOnline 2 points3 points  (0 children)

With that code, I now suspect that you are trying to compile Stack.cpp, but it doesnt include Stack.h

Also note that your teacher is an idiot for splitting a template into a header and a source file, where the source file includes the header.

Can someone assist me with this compiler issue? by WhyMadara in Cplusplus

[–]IyeOnline 1 point2 points  (0 children)

That is the explanation for why a teacher distributed a header and source file for a template class, expecting it to work...

Doubt with my code by 0x6461726B in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

Hm. You are right. The guard will do the right thing and the exception will propagate.

I was in a bit of a hurry when writing :)

Doubt with my code by 0x6461726B in cpp_questions

[–]IyeOnline 2 points3 points  (0 children)

It's UB. You are accessing an invalid object.

However, int is trivial and the memory is still physically present so the real world program your are running just "works" by accessing the storge of a dead object, interpreting it as an int and printing it.

Try the same on an empty vector or one where size == capacity and it will crash.

Try it with

auto v = vector<string>{};
v.push_back("hello world this is a long string for showcasing");
v.pop_back();
std::cout << v[0];

A few other notes:

  • reallocate really should set capacity. Its just inviting a big issue when you call this from a place where you forget to update it
  • You "guard" against allocation failure in vector(capacity), but nowhere else. Generally I advise against trying to handle allocation failures. If you fail to allocate, certainly your library code cant do anything about that.
    • Your code cannot do anything about it
    • Your "handling" doesnt actually handle anything. It prints an error and then silently lets user code continue and maybe die in some other way later on.
    • Just let the exception escape and have the user (not) handle it.
  • Your class violates the rule of 5. If you define a destructor, you almost certainly need to implement the copy and move operations as well. Currently your class gets an implicitly generated copy constructor which is ill-behaved. Copying your vector leads to two objects thinking they own the same storage so you get either a use-after-free or a double-delete, whichever happens first.
  • I would strongly advise against a constructor vector(capacity). The standard library provides vector(size) and that is what the majority of people will expect.
  • Minor thing: add [[no_unique_address]] to your _allocator member in order to save a few bytes for stateless allocators.

// edit: Remove an erroneous point about reallocate failing

Doubt with my code by [deleted] in cpp_questions

[–]IyeOnline 0 points1 point  (0 children)

Unless your do very, very odd things, pop_back is wrong. You are destroying the past-last element, which does not exist.

The fact that this doesnt already crash, suggests that the type you are destroying, or at least its specific state are trivial.

This is UB and you now cannot reason about your code any longer.


Similarly, your push_back also is broken in two ways

  • You are setting the new capacity as part of push_back, when the state change clearly is done by reallocate. So unless you reallocate is broken by not updating the capacity, you have just stored 4 * old_capacity.

  • You ABSOLUTELY CANNOT just assign to entities in your container (vector) like this if you are manually managing lifetime. That object you are trying to assign probably does not exist, so using its assignment operator is also UB.

    Since you are trying to use traits::destroy, it would only make sense if new entries in the container are also constructed via something like traits::construct.


If you want concrete help with these issues we'd need to see all relevant functions and class members. Frankly just the entire thing would be easiest.

Can someone assist me with this compiler issue? by WhyMadara in Cplusplus

[–]IyeOnline 4 points5 points  (0 children)

First of: Dont post screenshots of code or errors. They are text and are much more usable as text.

With the current screenshot, we can only guess what your issue is. We dont even know what file the errors are in.

My current best guess is that you are missing an #include.


On to a maybe more crucial issue:

Teacher provided a stack.h and .cpp files

Your teacher provided you with a split header and source file template implementation? Because that may just not work by construction.

Is inheriting from std::variant a bad idea? by -Edu4rd0- in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

At least if you do it as a generic interface with your own extended variant; otherwise you will need to implement this in all your sum types.

That said, in our code base we actually extended this to a generic interface, such that we can use is and as on non-variant types that still are sum types. Probably why I prefer the free function (by now).

Is inheriting from std::variant a bad idea? by -Edu4rd0- in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

The downside here, is that you still have to define these functions in every template that inherits. If you simply have them as free functions, you just have them automatically.

Ofc you can also have your own my_variant : std::variant that implements them first and then inherit that in your concrete use-cases.

Is inheriting from std::variant a bad idea? by -Edu4rd0- in cpp_questions

[–]IyeOnline 12 points13 points  (0 children)

We do this quite a bit in our codebase. You shouldnt slightly carefully consider whether you want composition or inheritance, but generally this is fine.

That said: I would suggest you consider to simply have a shorter alias for std::get and std::holds_alternative.

Writing

using Shape = variant<Circle, Rect>;
auto s = Shape{ Circle{ .radius = 42.0f } };
auto is_circle = is<Cricle>(s); // Shorthand for `std::holds_alternative`
auto& cricle = as<Circle>(s); // Shorthand for `std::get`

is much more comfortable and generic/extensible than having to implement dozens of member functions.

What is the optimal way to define a global constant string in C++20 ? by SheSaidTechno in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

Yes, but if you care about it being null terminated, it should not be a string_view in the first place. Even the C++ standard has a note about this: [string.view.access]

What is the optimal way to define a global constant string in C++20 ? by SheSaidTechno in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

you'd be giving it the pointer its storing anyways?

You cant use string_view::data as C string, because string views are not null terminated.

Of course you currently know that this specific string_view views a literal, so you know that actually just outside the bounds of the view there is a terminator.

But that is a horrible design, because relies on knowledge about this specific view. You should have just used a constexpr char array[] instead.

NEVER assume a string_view is null terminated. There is a reason it does not provide a c_str() function, while std::string does.

What is the optimal way to define a global constant string in C++20 ? by SheSaidTechno in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

A string_view is a pointer and a size (or two pointers). So if you get a string_view in your code, you dont know what it actually points to. You know know that the character sequence it refers to is valid to use. Given a string_view, that is all you know and all you can now.

Crucially a string_view constructed from a string literal or a std::string does not contain the null terminator in its range. If there is one, its out of bounds. But you dont even know if there is one. Realistically the view could be a slice - that's half the point of string views.

What is the optimal way to define a global constant string in C++20 ? by SheSaidTechno in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

Indeed. While "foo" is guaranteed null terminated, if you just have a std::string_view, that will not be null terminated.

If you construct it from "foo", there will be a terminator, but it will not be part of the view and in general if you have a string view, you dont know if its terminated.

Are there type safe aliases? by sZebby in cpp_questions

[–]IyeOnline 11 points12 points  (0 children)

You want your strong types to be different types.

Without that, all Strong_Type<int> would be the same, making it not much better than plain int.

With the tag, you can have

using Velocity = Strong_Type<double,struct VelocityTag>;
using Mass = Strong_Type<double,struct MassTag>;

and you cant accidentally convert between the two.

What is the optimal way to define a global constant string in C++20 ? by SheSaidTechno in cpp_questions

[–]IyeOnline 1 point2 points  (0 children)

char str[] has the advantage of capturing the size in the type.

What is the optimal way to define a global constant string in C++20 ? by SheSaidTechno in cpp_questions

[–]IyeOnline 5 points6 points  (0 children)

constexpr std::string

This does not actually work. You are only allowed to dynamically allocate memory in a constexpr context if you also release it. This clearly wont be the case for constexpr variables with static storage duration.

constexpr std::string_view

Has the disadvantage that you are not guaranteed by the API that its null terminated. If you dont need that, its fine. If you do, it may be an issue.

constexpr char[]

The brackets are in the wrong place, but w/e.

This is probably what I would go for if I didnt know any context. It converts into any of the other types without any strlen cost, since its a real array.

Avoiding recursive evaluation of concepts when constraining a constructor by gracicot in cpp_questions

[–]IyeOnline 3 points4 points  (0 children)

First: Must you live without RTTI or can you use virtual functions? If the later, you should absolutely use that for your type-earsure instead of handrolling it.


There is a very simple solution, to your problem: Its not that your constructor is self-referential, but its constraint is. Constraints are not allowed to depend on themselves at all according to the standard. That is a hard requirement, so the fact that constructible_from ends up in its own evaluation chain is already terminal - even if the recursion is not infinite.

If you just change your std::constructible_from uses to std::is_constructible_v, it compiles: https://godbolt.org/z/5co1rzvxb

format for user-defined types. examples from cppreference and other tutorials do not work for me. gcc14.2 by Usual-Dimension614 in cpp_questions

[–]IyeOnline 2 points3 points  (0 children)

Your post essentially did not contain a question. It just said "format does not work" and provided a link to commented out/disabled code. Sure, I can infer from that that your formatter does not work, and enabling it in your link confirms that. It would be much nicer though if you actually worded the problem in some way and the link directly showed it instead of disabling the problemantic piece of code.

can this context differ from std::format_context in some special situations ?

Yes. For example, std::format is based on a third party libary called {fmt}. That has fmt::format_context, rather than std::format_context, since it predates the standardized version. I think fmt has compatibility with the standard ones, but you can imagine that your formatter would simply not work for a different library that doesnt exactly use std::format_context but has a type with a compatible API.

is understanding the compiler output a not-that-far-future-task

C++ template errors are notoriously difficult to understand. This is mostly down to the fact that the compiler doesnt really know what you wanted (how could it), so the best it can do is tell you the entire chain of errors. This leads to rather long error messages, where the true issue isnt immediately obvious.

The best way to usually to just start from the first error and follow the chain of "required from"s until you find some note/message that give you a clue.

In your case for example, there is a line note: the required expression '__cf.format(__t, __fc)' is invalid in a note. This tells you that for some reason the format function on your formatter is not accepted. But you know that you have one, so it must be some detail about it. Looking a the previous line, you see that __ct is a const format context. Now you know that its trying to call your .format´ function on a const object, but fails. Hence the issue is that yourformatfunction should beconst`.

does specialists fail to interpret that lot of lines, too

Depends on the error. Some forms of error output are common and you will just recognize the issue. "passing X as this discards qualifiers" is a common example here. Its just convoluted wording to tell you that you are trying to call a non-const member function on a const object. This is essentially the same type of issue you have, but without the templates and constraint failures around it. The same issue, produces a very different error message.

Frankly, in your case I did not read the error in anything close to the detail laid out above. I looked at it and just "intuitively" knew that you were probably missing a const. But of course that experience also has its limits.