尾随返回类型、声明和引用限定符:它们能一起工作吗?

Trailing return type, declval and reference qualifiers: can they work together?

本文关键字:一起 工作 返回类型 声明 引用      更新时间:2023-10-16

考虑以下示例:

#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转换为右值引用类型。
如果我们像下面这样稍微改变一下,我们就会遇到相反的问题:

// ...
template<typename T>
auto f() -> decltype(std::declval<T&>().f())
{}
// ...
int main() {
    f<A>();
    f<B>();
    // f<C>(); // (*)
}

现在(*)的行不能工作,因为std::declval在特定情况下将类型转换为左值引用类型。

有没有办法定义一个表达式,如果它有一个成员函数f,不管它的引用限定符是什么,它都接受类型T ?


<子>我没有任何实际的案例可以使用它,我也不能给出任何实际的使用例子。
这个问题只是出于好奇,别无其他。我理解如果修饰符存在是有原因的,我不应该试图破坏类的设计。

构建一个类型trait,如果表达式declval<T>().f(declval<Args>()...)是一个有效调用,则返回true。然后传入U&U&&,表示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&&>>>;
}

您可以使用Boost。Hana的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>()))
{}
演示