I tried to reduce my problem to a minimal example
#include <string>
#include <utility>
template <typename T> struct some_traits;
template <typename T> using traits = some_traits<std::remove_cvref_t<T>>;
struct reference_struct {
std::string s;
};
template <> struct some_traits<reference_struct> {
template <typename T> // T is constrained to reference_struct
static auto &&get(T &&t) {
return std::forward<T>(t).s;
}
};
struct by_value_struct {
std::string s;
};
template <> struct some_traits<by_value_struct> {
template <typename T> // T is constrained to by_value_struct
static auto get(T &&t) {
return t.s;
}
};
template <typename T> static auto &&element(T &&t) { return traits<T>::template get(std::forward<T>(t)); }
// overload that returns by value
template <typename T> static auto element(T &&t) { return traits<T>::template get(std::forward<T>(t)); }
int main() {
reference_struct r;
by_value_struct b;
auto &&reference_result = element(r);
auto by_value_result = element(b);
}
In the example, we see some_traits, which can be specialized for different structs to implement different static get methods.
Then there is a universal global auto&& element(T&&) function, where T is a struct with some_traits implemented (in the actual code T is constrained using concepts).
Some structs might implement some_traits::get by returning by reference, others might return by value.
auto get is used because the structs can return any type, not just std::string.
The above code does not compile, because calls to get are ambiguous. If the "overload that returns by value" is not present, there is of course a compiler warning stating that element returns a reference to a temporary. I tried specializing:
template <> static auto element(by_value_struct&& t) { return traits<by_value_struct>::template get(std::move(t)); }
template <> static auto element(by_value_struct& t) { return traits<by_value_struct>::template get(t); }
template <> static auto element(const by_value_struct& t) { return traits<by_value_struct>::template get(t); }
But the template specializations could not be matched due to the return type mismatch (auto vs auto&&).
My question is, can I create a function with a more general interface, which returns either by universal reference or by value (copy)? I mean in a way the outside user does not have to know beforehand which one will get returned.