On customization points and niebloids
#44
Replies: 6 comments
-
The current approach to static dispatch in |
Beta Was this translation helpful? Give feedback.
-
P2547 has a very interesting summary of why ranges CPOs are not a panacea:
|
Beta Was this translation helpful? Give feedback.
-
Hmm, I don't follow why the compiler wouldn't be able to see right through. |
Beta Was this translation helpful? Give feedback.
-
Copy elision only works with pass-by-value semantics. If we're taking a reference, objects cannot be directly constructed into the storage where they would be copied/moved to. Even if we try to forward the reference by casting it to rvalue reference, that becomes an xvalue, not a prvalue. Just to double check, I wrote this example (godbolt link): Compiler flags: #include <string>
#include <iostream>
namespace _foo {
void foo();
struct _fn {
template<typename T, typename V>
requires requires (T& obj, V&& value) {
foo(obj, (V&&)value);
}
void operator()(T& obj, V&& value) const {
foo(obj, (V&&)value);
}
};
}
inline namespace _foo_cpo {
inline constexpr _foo::_fn foo{};
}
struct string_wrapper {
string_wrapper(std::string str_) : str{str_} {
std::cout << "constructing\n";
}
string_wrapper(const string_wrapper& other) : str{other.str} {
std::cout << "copying!\n";
}
string_wrapper(string_wrapper&& other) : str{std::move(other.str)} {
std::cout << "moving!\n";
}
std::string str;
};
struct my_type {
friend void foo(my_type& self, string_wrapper value) {
std::cout << value.str;
};
friend void bar(my_type& self, string_wrapper value) {
std::cout << value.str;
};
};
int main() {
my_type t;
foo(t, string_wrapper(std::string{"hello\n"})); // will result in an extra call to
// string_wrapper's move constructor.
bar(t, string_wrapper(std::string{"bye\n"}));
} Output:
|
Beta Was this translation helpful? Give feedback.
-
Ah, I was thinking of RVO as applied to the return value of the CPO in an eligible scenario, not to temporaries used for argument passing. Yes, it's pass-by-value semantics or nothing. |
Beta Was this translation helpful? Give feedback.
-
Closed by #272 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Customization points and ADL
Customization points are hooks used by generic code that end-users can specialize to customize the behavior for their types. The STL provides plenty of examples:
begin
,end
,swap
, etc.Argument-dependent lookup is a set of rules for looking up the unqualified function names in function-call expressions, including implicit function calls to overloaded operators and is very relevant when implementing customization points.
If one were to make a call to a customization point function in generic code, the correct way to do it is the following:
This is known as the std-swap-two-step idiom and is used within the STL itself (see
Swappable
). The alternatives are incorrect (in a generic context) for the following reasons:It's really a shame that the correct way to do this is not intuitive and, when a library author provides customization points based on pre-C++20 standard patterns, it's the user's responsibility to do the right thing.
Niebloids!
Eric Niebler proposed a solution for this when designing the
range-v3
library (and in 2018, he also suggested a name for it π ).Now, instead of having a function, we have a global functor (called customization point object or CPO) that has the following properties:
The responsibility of doing the right thing is back in the hands of the library author, making the API safer. And as a bonus, they work really well with concept checking.
From C++20 onwards, we have CPOs in the standard library, i.e. see
std::ranges::swap
.But wait, there is more...
Beta Was this translation helpful? Give feedback.
All reactions