如何绕过在 decltype 表达式中指定变量

How can I get around specifying variables in decltype expressions?

本文关键字:变量 表达式 何绕过 decltype      更新时间:2023-10-16

>假设我有以下示例函数:

template <typename Fn> auto call(Fn fn) -> decltype(fn()) {
  return fn();
}

此函数的重要之处在于其返回类型取决于其模板参数,这是可以推断的。因此,最终,返回类型取决于函数的调用方式。

现在,我们还有一个测试类:

struct X {
  int u;
  auto test() -> decltype(call([this]() -> double {this->u = 5; return 7.4;})) {
    return call([this]() -> double {this->u = 5; return 7.4;});
  }
};

如您所见,X::test调用call,返回相同的返回值。在这种情况下,返回类型很简单地给出 double ,但让我们假设我们不知道call做什么,并且 lambda 具有更复杂的返回类型。

如果我们尝试编译它,编译器会抱怨,因为我们在顶层使用this(而不是在允许表达式的作用域中(:

error: lambda-expression in unevaluated context
error: invalid use of ‘this’ at top level

但是,我必须使用我传递给call的lambda的捕获,以便正确获得call的返回类型。您建议如何在离开lambda的同时解决此问题?

注意:当然,我可以将 lambda 移动到某种帮助程序类型的operator(),我使用 this 指针的副本实例化它,但我想避免使用那个样板文件。

我认为真正需要关注的错误是"未求值上下文中的lambda表达式"。您不能在未计算的上下文中使用 lambda,因为每个 lambda 表达式都有一个唯一的类型。也就是说,如果允许decltype([]{}),它将推断出与在其他上下文中[]{}不同的类型。即 decltype([]{}) fn = []{};行不通。

除非您只想显式编写返回类型而不是推导它,否则我认为您别无选择,只能创建一个可以在您需要的上下文中使用的真实类型,无论需要什么样板。

尽管如果将test更改为不是成员函数是可以接受的,那么您可以使用 lambda 可以通过省略它来推断其返回类型的事实,如果主体只是一个 return 语句:

template <typename Fn> auto call(Fn fn) -> decltype(fn()) {
    return fn();
}
struct X {
    int u;
};
int main() {
    auto test = [](X *x) { return call([x]() -> double {x->u = 5; return 7.4; });};
    X x;
    test(&x);
}

如果函数的尾随返回类型语法具有相同的属性,那就太好了。我不知道为什么没有。

似乎是一个编造的(解释的,人为的(问题,因为

  • 如果您从其他地方获得 lambda,那么它将被命名,绑定this没有问题。

  • 如果您没有从其他地方获取 lambda,那么您就知道结果类型。

简而言之,正如目前所述问题(当我写这个答案时(,除了你自己的意愿强加的问题之外,没有问题。

但是,如果您坚持这一点,那么,只需将this作为参数传递,而不是通过lambda定义绑定它。然后,对于 call 的调用,绑定参数。但是,也许不用说,既然这只能解决一个虚构的问题,那么它就是一个真正的鲁布·戈德堡(Rube Goldberg(结构,一个体面的过度开花的不必要的复杂性,除了它自己的复杂性之外,它没有解决任何真正的问题。

如果有的话,最初的真正问题是什么?

你不应该总是复制并粘贴函数体来decltype .引入后期指定的返回类型的要点是,您将能够以某种方式从参数推断出正确的返回类型。
例如 auto f(T x) -> decltype(g(x)) { return h(), g(x); },而不是-> decltype(h(), g(x))

因此,在您的情况下,double test()就足够了,因为我们知道call的行为,并且我们知道我们传递给它的 lambda 函数的返回类型。
在更复杂的情况下,我们应该通过使用有关call和其他东西的知识来减少decltype中的代码。