
czw., 19 gru 2024 o 18:04 Vinnie Falco via Boost <boost@lists.boost.org> napisał(a):
On Thu, Dec 19, 2024 at 8:19 AM Christian Mazakas via Boost < boost@lists.boost.org> wrote:
My point was that users don't want this compared to the method chaining version and I think the code here kind of speaks for itself.
users.try_find().map() is the preferred API over try_find(user).map().
In C++, free functions which operate on a concrete type or a type which matches named requirements are considered to be part of the public API for that type. std::swap is a good example, this is why cppreference.com lists associated free functions under a section titled "Non-member functions:"
https://en.cppreference.com/w/cpp/container/vector
Good library design means designing for C++. Therefore:
Free functions should be considered equally ergonomic to member functions.
Good encapsulation means giving an algorithm access to as few implementation details as possible. Therefore:
Prefer free functions when an implementation can use only the publicly visible members of a type.
Scott Meyers elaborates on this:
https://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/1844011...
When designing a library you have to assume that the user knows C++. If they don't understand the use of free functions, the solution is that they should educate themselves, not to always use member functions. The free function is better than the member function for several reasons:
1. The implementation only needs to be written once 2. Authors of new containers have less to implement 3. The header file for the containers does not change; no need to include <optional> 4. Multiple free functions can exist, each returning their own optional type, permitting std::optional and boost::optional to co-exist harmoniously. 5. Better encapsulation: the free function does not have access to private members 6.
A common objection to the use of free functions is that the member function is more discoverable, such as in an IDE. This problem is better solved in the IDE, of which there are few, rather than solving it in the container of which there are uncountably many. That is, if someone was to type "u." in the IDE, then the autocomplete feature could show associated free functions in addition to member functions. And when an associated free function is selected, the IDE will change the member function call to become the free function call syntax.
For all the reasons listed above, the free function is preferable to the member function. As this allows the functionality of containers to be extended without imposing additional work on all existing and future container authors. And I do believe that we need better educational resources, so the myth that member functions are somehow more ergonomic can be decisively dismissed.
I would like to offer some philosophical remarks here, which are no longer tied to optional<T&> or map, so they deserve a separate thread. In C++ we have what we have, but technically we could think about "encapsulation" and "member function notation" as two orthogonal things. This already works one way in C++: you can have friend functions, which have access to private state but force their users to use the free function notation. This doesn't work the other way though. I am personally not sold by the "discoverability" argument, because I mostly code in vim where I do not get any hints from the IDE anyway. I do not care that much about the member function notation, but I do care about the left-to-right notation. This is because I come from the latin culture, where we are taught to read, write, and think left-to-right. So reading "find value, transform the value" comes naturally, and the notation where "find" is left of "transform" becomes more intuitive. This doesn't mean that I like member functions (because they involve encapsulation), or that I like the pipe notation (because of the error messages nightmare). But if there was a left-to-right solution devoid of the above problems, I would take it. Regards, &rzej;