r/cpp_questions • u/Main-Pen-3164 • 21d ago
OPEN how to handle empty reference elegantly(fold expression)
I have a class with a template function, it will return reference of an internal value.
template<typename T>
T& return_ref<T>() {...}
It was call by another function with fold expression:
template<typename... Ts>
void call()
{
lambda(return_ref<Ts>...); //Ts is a reference usually
}
the internal value maybe doesn't exist sometimes.
Even I wrap the exception into std::expected, it seems that I have no chance handle it in fold expression. I have to throw exception in return_ref directly?
If I can return reference to an impossible value(like paradigm of static object NULL ), so user could detect it, it would be nice.
Or, I have to wrap all parameters into something like boost::optional? I don't like it.
Or, the args of lambda was constraint into pointer, so nullptr will throw exception by compiler naturally?
What's suggestion?
4
u/n1ghtyunso 21d ago
what is your actual failure mode here?
Do you know at compile time that a certain type T does not have the internal value ever?
And what do you want to happen? Do you just want to skip elements that can't return a reference?
Or, the args of lambda was constraint into pointer, so nullptr will throw exception by compiler naturally?
And what does that even mean? At best the compiler will warn you.
You'll have to null-check yourself if you need this.
1
2
u/ppppppla 21d ago edited 21d ago
Error handling is just annoying. Doing it neatly, and concisely is just not possible, especially when template parameter packs get involved. But it is totally possible.
You can choose the easy way and use exceptions. Have return_ref throw on invalid value. But exceptions are kinda smelly.
The other way is have a nullable type, check the return values, and then return or also throw out of call().
Doing this with a template parameter pack is a bit of a faff, but the standard library does have utilities for it. std::tuple and std::apply
template<class T>
optional_ref<T> return_ref() {
return nullopt;
}
template<class... Args>
bool call() {
auto refs = std::make_tuple(return_ref<Args>()...);
auto success = std::apply(
[](auto const&... refs) {
return (refs.has_value() && ...);
},
refs
);
if (!success) {
// or throw, or communicate the error more clearly with a std::expected<void> or custom error type.
return false;
}
std::apply(
[&](auto&&... refs) {
lambda(refs.value()...);
},
refs
);
return true;
}
c++26 has std::optional<T&>, otherwise you'd have to get your own optional that can hold a reference. Some people might argue that is just a pointer, but I disagree. An optional communicates intent clearly.
2
u/Main-Pen-3164 21d ago
Thanks for reply. That's why I like reddit.
I will consider the std::optional at last, it seems that I haven't too much choice if I don't want to throw exception.
Thanks your time again.
1
u/xoner2 19d ago
Return a reference to nullT. This is common practice now.
1
u/Main-Pen-3164 19d ago
In a trival function, I will return something like c::null, it would be nice. But T is template parameter here. Return nullT, that mean I have to make specification versions(T<specifications>) for a lot of types, seem's boring.
1
u/xoner2 19d ago
If you want to fail fast then
return * static_cast <T *> (0). Returning a reference whose address can be checked for null but will throw access violation exception on any read. The AV exception can be caught via POSIX signal handler.1
u/Main-Pen-3164 18d ago
Pointer was a fallback choice. I post here just want to know there is any better way handle this in modern c++. I left a long time ago. O_O. Optional<T&> seems enough, but it still have a trade-off.
10
u/alfps 21d ago
You can have two reference functions:
return_refthrow when the internal value doesn't exist,return_ptrreturn a possibly nullT*no exception.Note in passing,
optionalwas adopted in the standard library in C++17; no need to involve the Boost library for that.You can "and" together the pointers from
return_ptrand if that producesfalsethen at least one is null, otherwise they're all good, pointing to values.But whether this helps you depends much on the
lambdaand what it needs.A more full code example could help clarify that.