On function_ref
and string_view
function_ref
and string_view
At C++Now 2018, I wrote three blog posts during the conference. This year I was somehow so busy at C++Now that I merely wrote a list of things I ought to blog about once I got the time. So here’s the first one.
On Wednesday afternoon,
Vittorio Romeo gave a very good summary of his proposed function_ref
. It’s a very well-defined type;
because it is non-owning and trivially copyable, it can sidestep the majority of design decisions that
are the bugbear of std::function
’s awkward design. (See
“The space of design choices for std::function
,”
2019-03-27.)
However, during the talk, David Sankel made an interesting observation. Consider:
template<class T>
T one(std::reference_wrapper<T> arg) {
T local = arg;
return local;
}
int main() {
std::string s = one("hello"s);
std::cout << s << "\n";
}
arg
is a reference to a temporary, but by converting it to T local
, we get a copy
by value.
Or consider:
std::string two(std::string_view arg) {
std::string local(arg);
return local;
}
int main() {
std::string s = two("hello"s);
std::cout << s << "\n";
}
arg
is a view to a temporary, but by converting it to std::string local
, we get a copy
by value. (Notice that this conversion is marked explicit
, so std::string local = arg;
won’t compile — thanks to Jason Cobb for the correction!)
But consider:
std::function<int()> three(std::function_ref<int()> arg) {
std::function<int()> local = arg;
return local;
}
int main() {
std::function<int()> f = three([x=42](){ return x; });
std::cout << f() << "\n";
}
arg
is a view to a temporary. By converting it to std::function<int()> local
,
we get a copy of the view — the resulting std::function
has reference semantics,
and when we call it on the last line of main
, we dereference a dangling reference and
produce undefined behavior.
This interaction is cute, but I definitely don’t want to imply that function_ref
(or function
)
should be derailed over it. The conclusion falls out fairly intuitively from the fact that
string_view
is a view over data, whereas function_view
is a view over code.
Data can be serialized; therefore it can be copied, and also compared for equality
(remember, “copies compare equal”).
Therefore the conversion from string_view
to string
has value semantics, and both
string
and string_view
provide an operator==
.
Code cannot be serialized; therefore it cannot be copied, nor can it be compared for equality.
Therefore the conversion from function_ref
to function
(or from reference_wrapper
to function
)
has reference semantics, and neither function
nor function_ref
provide an operator==
.
So, to a first approximation, function_ref
is to function
as string_view
is to string
— both are
parameter-only types with reference semantics. But watch out for the inherent difference between
“views over data” and “views over code”!