Конечный тип возврата, declval и ссылочные квалификаторы: могут ли они работать вместе?

Рассмотрим следующий пример:

#include <utility>

struct A { void f() {} };
struct B { void f() & {} };
struct C { void f() && {} };

template<typename T>
auto f() -> decltype(std::declval<T>().f())
{}

int main() {
f<A>();
// f<B>(); // (*)
f<C>();
}

Когда вызывается с B (линия (*)), код больше не компилируется для std::declval новообращенные T на тип ссылки rvalue в конкретном случае.
Если мы немного изменим его следующим образом, у нас возникнет противоположная проблема:

// ...

template<typename T>
auto f() -> decltype(std::declval<T&>().f())
{}

// ...

int main() {
f<A>();
f<B>();
// f<C>(); // (*)
}

Теперь линия на (*) не будет работать для std::declval преобразует тип в тип ссылки lvalue в конкретном случае.

Есть ли способ определить выражение, которое принимает тип T если он имеет функцию-член fнезависимо от того, что его эталонный классификатор?



У меня нет ни одного реального случая, в котором я бы использовал это, и я не могу привести ни одного реального примера использования.
Этот вопрос ради любопытства, не более того.
Я понимаю, что есть причина, если реф-классификатор есть, и я не должен пытаться нарушать дизайн класса.

4

Решение

Создайте черту типа, которая возвращает выражение true, если declval<T>().f(declval<Args>()...) действительный звонок Тогда пройдите в U& а также U&& указание объекта типа lvalue или rvalue T,

namespace detail{
template<class...>struct voider{using type=void;};
template<class... Ts>using void_t=typename voider<Ts...>::type;

template<template<class...> class, class=void, class...>
struct can_apply : false_type { };

template<template<class...> class L, class... Args>
struct can_apply<L, void_t<L<Args...>>, Args...> : true_type {};

template<class T>
using rvalue = decltype(declval<T>().f());
template<class T>
using lvalue = decltype(declval<T&>().f());

template<class T>
using can_apply_f
= integral_constant<bool, detail::can_apply<rvalue, void_t<>, T>{} ||
detail::can_apply<lvalue, void_t<>, T>{}>;
}

template<class T>
enable_if_t<detail::can_apply_f<T>{}>
f();

В C ++ 17 это становится немного проще:

namespace detail{
auto apply=[](auto&&g,auto&&...xs)->decltype(decltype(g)(g).f(decltype(xs)(xs)...),void()){};

template<class T>
using ApplyF = decltype(apply)(T);

template<class T>
using can_apply_f = std::disjunction<std::is_callable<ApplyF<T&>>, std::is_callable<ApplyF<T&&>>>;
}
2

Другие решения

Вы можете использовать Boost.Hana’s overload_linearly протестировать оба варианта:

template<typename T>
using lval_of = add_lvalue_reference_t<decay_t<T>>;

auto call_f = hana::overload_linearly(
[](auto&& t) -> decltype(move(t).f()){},
[](auto&& t) -> decltype(declval<lval_of<decltype(t)>>().f()){}
);

template<typename T>
auto f() -> decltype(call_f(declval<T>()))
{}

демонстрация

0

По вопросам рекламы [email protected]