1

假设我有以下示例功能:

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

但是,我必须使用传递给的 lambda 的捕获call来获得call正确的返回类型。你会建议如何解决这个问题,同时仍然离开 lambda?

注意:当然,我可以将 lambda 移动operator()为某种辅助类型,我用this指针的副本对其进行实例化,但我想避免使用该样板。

4

3 回答 3

2

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

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

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

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);
}

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

于 2012-05-11T13:53:21.727 回答
0

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

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

于 2012-05-11T13:42:59.953 回答
0

这似乎是一个虚构的(解释的,人为的)问题,因为

  • 如果您从其他地方获得 lambda,那么它会被命名并且没有问题 binding this

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

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

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

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

于 2012-05-11T13:16:29.720 回答