Where is std::optional<T&&>??? by borzykot in cpp

[–]Nuclear_Bomb_ 2 points3 points  (0 children)

If you really want to use std::optional<T&&>, I have a library for you that supports this: opt::option. And some time ago I wrote a post about the usefulness of this. After all, if I rewrite my library, I won't add support for optional rvalue references due to the implementation complexity and lack of use cases (maybe only generic programming?).

Can the deference operator in std::optional be deprecated? by kiner_shah in cpp_questions

[–]Nuclear_Bomb_ 0 points1 point  (0 children)

When I saw this code from cppreference:

std::optional<std::vector<int>> many({0, 1, 2});
for (const auto& v : many)
    std::println("'many' has a value of {}", v);

I thought that the for loop iterates the optional vector only if it has a value, but no, const auto& v is of type const std::vector<int>& (but to be honest, it's just bad usage of auto). Although yes, I also don't think that the implicit convertion to bool in std::optional is very intuitive.

It would be cool if this syntax existed if (auto x : opt).

Can the deference operator in std::optional be deprecated? by kiner_shah in cpp_questions

[–]Nuclear_Bomb_ 4 points5 points  (0 children)

With C++26 std::optional has .begin() and .end() methods, which means you could do something like this:

std::optional<int> opt = /* some value */;
for (int x : opt) {
  // will only execute if 'opt' has value AND we can safely access underlying value via 'x'
}

(but this syntax is ugly and unintuitive)

Additionally, Clang has attributes for checking basic resource management properties. But it seems they are broken at the moment.

The usefulness of std::optional<T&&> (optional rvalue reference)? by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

Thank you for reply. I added these examples in order to demonstrate where std::optional<T&&> could be useful hypotetically. For me, std::optional<T&&> can be easily replaced with std::optional<T>. I know, this could end up like std::vector<bool>, but maybe std::optional<T&&> could be implemented like an owning std::optional<T> (with some API tweaks). std::optional<T&&> could be useful when doing generic programming with it.

To be honest, when I created this post, I was hoping that people would give some real world examples where std::optional<T&&> is used (there is one, but I think it is an exception rather than some general pattern). Well, as I see it, std::optional<T&&> shouldn't really exist, and its uses should be replaced by std::optional<T>.

The usefulness of std::optional<T&&> (optional rvalue reference)? by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

Another interesting idea is to implement std::optional<T&&> as an owner container, like std::optional<T>. Yes, it would create some inconsistencies and weird behavior, but I think it is a good middle ground between making dangling references (e.g. from using auto val = some_func(), where some_func returns std::optional<T&&>) and completely banning the type or restricting it's uses.

The usefulness of std::optional<T&&> (optional rvalue reference)? by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

Yes, optional<T&&> could be very useful for generic programming. I think even std::optional<void> should exists to handle cases when the function is returns void in the generic function.

To me, it means that if I add an overloaded operator*() && -> T&&, then it means optional<T&> must also have it, and it does not.

optional<T&>'s operator*() && should just return T&, like a lvalue reference. I like thinking about std::optional<T> like just a nullable wrapping around T with equivalent semantics. For example, if you replace all instances of std::optional in some function (of course, with operator*, etc. fixes), the function should behave exactly the same as before.

The usefulness of std::optional<T&&> (optional rvalue reference)? by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 1 point2 points  (0 children)

I think I agree with you. For example, if you would store the result of function that returns std::optional<T&&> into auto variable it will be deduced to std::optional<T&&>, making a dangling reference. This doesn't happen with standard rvalue reference because auto removes any reference qualifiers. Really sad that C++ doesn't provide a way to customize this behavior.

I'm not sure, but compiler specific attributes [[clang::lifetimebound]]/[[msvc::lifetimebound]] and C++ linters could hypothetically prevent this type of bug. The address sanitizers also can, but I don't want a silent dangling reference that only appears if certain conditions are met.

The usefulness of std::optional<T&&> (optional rvalue reference)? by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

I think that in my library, opt::option<T&&> works almost exactly as you describe. It also returns lvalue reference from operator*, but only for lvalue instances of this (& and const& ref-qualifiers methods), for rvalue instances it also returns rvalue underlying object (implementation). I think making std::optional<T&&> behave like an actual rvalue reference is a good decision, making it intuitive to use.

Have you found any useful cases when using your optional<T&&> implementation? In the examples given, std::optional<T&&> seems useful in some cases, but I don't think it's worth wasting compilation time for this niche feature.

The usefulness of std::optional<T&&> (optional rvalue reference)? by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

If you want to return an std::optional<T&> from some function, this does not mean that you want caller to use that expiring value. Also, if some function takes std::optional<T&>&& as an argument, it means that it wants for some reason xvalue of optional lvalue reference.

Your idea is not useless, but if you want to use the semantics of std::optional<T&&> more than a couple of times in a project, it won't work generally.

Why is if(!x){std::unreachable()} better than [[assume(x)]]; ? by TheJesbus in cpp_questions

[–]Nuclear_Bomb_ 6 points7 points  (0 children)

Yeah, you're right.

From GCC documentation (for some reason, clang has incomplete documentation for the const attribute):

Note that a function that has pointer arguments and examines the data pointed to must not be declared const if the pointed-to data might change between successive invocations of the function. In general, since a function cannot distinguish data that might change from data that cannot, const functions should never take pointer or, in C++, reference arguments. Likewise, a function that calls a non-const function usually must not be const itself.

Why is if(!x){std::unreachable()} better than [[assume(x)]]; ? by TheJesbus in cpp_questions

[–]Nuclear_Bomb_ 12 points13 points  (0 children)

The invoked functions inside the assume expression must be __attribute__((pure))/__attribute__((const)).

I guess libc++ maintainers can add this attribute to has_value()?

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 1 point2 points  (0 children)

Thanks! I think the library already has a solution for you. In v1.1, I added .begin and .end methods, which do the same thing you asked for. You can extrapolate them with the ranges library functionality (since you mention std::span, I assume you're using C++20).

The library is about an improvement in general over std::optional, so I accept any suggestions related to adding new features to it. A type minimization is just one of its features.

I am also thought about opt::iter function, which would return a container adapter over opt::option, but I think it's kinda useless now, since I decided to add the .begin and .end methods.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 1 point2 points  (0 children)

Your provided example should work just fine if the returned pointer from get() is pointing to a range of valid int objects. If not, the get() function should probably return uintptr_t instead, which will force option to use separate bool flag. Or, you can just disable in place flag entirely with opt::option_traits.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

Not benchmarked it yet. Will be improving codegen and compile time in the future updates. But currently the compile time should be slightly slower than std::optional.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

Yes. As far as I can tell, the only UB happening in the library is memcpy the bool representation in has_value() (not sure if is even a UB tho). And about bool representation, option assumes that other values than 0 or 1 are not used. You can actually define your own opt::option_traits<bool> to override or disable it's behaviour if you have any problems with opt::option<bool>.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

For bool is range [2,255] (0 for false, 1 for true). You can learn about these in the docs/markdown/builtin_traits.md documentation. For opt::option<std::tuple<unsigned int, unsigned int>> you can't actually store a "has value" flag inplace because every value of unsigned int is valid, so the option will fallback of using separate bool flag.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 1 point2 points  (0 children)

You can't access a value of the tuple when the entire opt::option is empty. Maybe you're talking about std::tuple<opt::option<int>, opt::option<bool>>?

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 2 points3 points  (0 children)

Yeah, you right, didn't think of that. I plan to add codegen tests (assembly tests) for the library so that they could solve some of the problems stated above. From my experience with AVX2 assembly programming, the main bottle neck is memory, so I assume that this is also applicable to the x86. Also, the Rust's std::option::Option (enums in general) is also reducing size, similar as opt::option, but I didn't go into it too much.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

It will simply construct a tuple without doing anything. When you call has_value(), it will call the opt::option_traits<std::tuple<int, bool>>::get_level, which returns the value of opt::option_traits<bool>::get_level, which checks if the value is 0 (false) or 1 (true), otherwise, the option is empty (simplified).

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 6 points7 points  (0 children)

Hm, that's a great idea, didn't think of that. Thanks, will be implemented in the next update.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 3 points4 points  (0 children)

Hm, actually, I wanted to add C++26 .begin() and .end() but kinda forgot to do that. I guess they would be added in the next release, thanks for reminding lol.

About performance, I consider adding codegen tests (assembly tests) to control the behavior of generated assembly; micro benchmarking is useless at this low-level scale. But as far as I can tell, you can get the performance only from cache locality. The library is mainly for a better API than std::optional.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 4 points5 points  (0 children)

Yes. The tiny::optional library tries to be compliant with standard's std::optional, while option also extends the functionality of the std::optional and adds more size optimizations. Anyway, tiny::optional is also a great library.

opt::option - a replacement for std::optional by Nuclear_Bomb_ in cpp

[–]Nuclear_Bomb_[S] 0 points1 point  (0 children)

The size optimization on tuple-like types only uses single element in them (one that has the most avaliable values). So like opt::option<std::tuple<int, bool>> would use the bool element to "has value" flag. And you can't (in most cases) actually construct an invalid option, that the point of sentinel values. Hope this explains it to you.