Здравствуйте, rg45, Вы писали:
R>Так опрятнее, по-моему. И перфект-форвардинг референс в параметре снимает головную боль с контанстностью и lvalue-rvalue.
Это обсуждаемый момент.
Начиная с того, что автор кода может знать ожидаемую семантику foo — как минимум, какая там должна быть константность.
Ну и rvalue кортеж отдаст свои элементы как rvalue reference, которые внутри выражений станут неконстантными. Насколько это то, что нам надо?
Можно для надёжности обмазать форвардом:
#define FWD(arg) (std::forward<decltype(arg)>(arg)) // кстати, удобная и полезная конструкция
[](auto&& t, int) -> decltype(FWD(t).foo(0)) { return FWD(t).foo(0); }
Но опять же, этот код — демонстратор технологии, а не библиотека в продакшен.
Её есть куда вылизывать ещё дальше.
Мы даже вот такое можем сделать
#define TRY_EVAL(expr) [](auto&& t, int) -> decltype(expr) { (expr); }
И ещё можно подумать, как сделать overloaded со строгим порядком предпочтений.
https://gcc.godbolt.org/z/4rrx4n1xc
#include <cstdio>
#include <type_traits>
#include <utility>
#include <tuple>
#define FWD(v) std::forward<decltype(v)>(v)
#define EXPLICITLY(expr) -> std::type_identity_t<decltype(expr)> { return expr; }
#define IMPLICITLY(expr) { return expr; }
template<size_t N> using index_t = std::integral_constant<size_t, N>;
template<class... Funs>
struct overloaded {
std::tuple<Funs...> funs;
overloaded(const Funs&... funs) : funs{funs...} {}
static_assert(sizeof...(Funs) != 0);
static constexpr size_t LAST = sizeof...(Funs) - 1;
template<size_t I>
auto call_(int, index_t<I> index, auto&&... x) const // int предпочтительнее, чем long, но делаем SFINAE
EXPLICITLY(std::get<I>(funs)(FWD(x)...))
template<size_t I>
decltype(auto) call_(long, index_t<I> index, auto&&... x) const
IMPLICITLY(call_(0, index_t<I+1>{}, FWD(x)...))
decltype(auto) call_(long, index_t<LAST> index, auto&&... x) const
IMPLICITLY(std::get<index.value>(funs)(FWD(x)...))
decltype(auto) operator()(auto&&... x) const
IMPLICITLY(call_(0, index_t<0>{}, FWD(x)...))
};
template<class... Funs> overloaded(Funs...) -> overloaded<Funs...>;
void foo(int x) { printf("foo(%d)\n", x); }
void bar(const char* x) { printf("bar(\"%s\")\n", x); }
void buz(const void* x) { printf("buz(%p)\n", x); }
void def(...) { printf("def(...)\n"); }
int main() {
overloaded o {&foo, &bar, &buz, &def };
printf("-----use:-----\n");
o(123);
o("xxx");
o(&o);
printf("-----misuse:-----\n");
o(123.45); // foo(123)
o(nullptr); // bar("(null)")
printf("-----default:-----\n");
o(&foo);
o();
o(1, 2, 3);
}