bitmask
bitmask

Reputation: 34636

How can I get around specifying variables in decltype expressions?

Assume I have the following exemplary function:

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

The important thing about this function is that its return type depends on its template parameter, which can be inferred. So ultimately, the return type depends on how the function is called.

Now, we also have a test class:

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

as you can see, X::test calls call, returning the same return value. In this case, the return type is trivially given as double, but let's assume for a bit we didn't know what call does and that the lambda has a more complicated return type.

If we try to compile this, the compiler will complain, because we're using this at the top level (not in a scope that would allow an expression):

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

However, I have to use the capture of the lambda which I pass to call in order to get call's return type right. How would you suggest to get around this, while still leaving the lambda?

Note: Of course I could move the lambda to be an operator() of some helper type, which I instantiate with a copy of the this pointer, but I'd like to avoid that boilerplate.

Upvotes: 1

Views: 856

Answers (3)

bames53
bames53

Reputation: 88175

I think the real error to be concerned about is "lambda-expression in unevaluated context". You can't use a lambda in an unevaluated context because every lambda expression has a unique type. That is, if decltype([]{}) were allowed it would deduce a different type than []{} in some other context. I.e. decltype([]{}) fn = []{}; wouldn't work.

Unless you want to just explicitly write the return type rather than have it deduced, I don't think you have any choice but to create a real type that you can use in the contexts you need, with whatever boilerplate that entails.

Although if changing test to not be a member function is acceptable then you could use the fact that lambda's can deduce their return type by omitting it if the body is only a single return statement:

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

It would be nice if the trailing return type syntax for functions had the same property. I'm not sure why it doesn't.

Upvotes: 2

Abyx
Abyx

Reputation: 12918

You shouldn't always copy-and-paste function body to decltype. The point of introducing late-specified return type was that you'll be able to somehow infer correct return type from arguments.
e.g. auto f(T x) -> decltype(g(x)) { return h(), g(x); }, not -> decltype(h(), g(x))

So, in your case, double test() is enough, because we know behavior of call and we know return type of lambda function we pass to it.
In more complex case, we should reduce code inside decltype, by using knowledge about call and other stuff.

Upvotes: 0

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145279

It seems to be a made up (construed, artificial) question, since

  • If you get the lambda from somewhere else, then it's named and no problem binding this.

  • If you're not getting the lambda from somewhere else, then you know the result type.

In short, as the problem is currently stated (as I'm writing this answer) there's no problem except one imposed by your own will.

But if you insist on that, well, just pass this as an argument instead of binding it via the lambda definition. Then for the call to call, bind the argument. But, perhaps needless to say, since that only solves a made-up problem it's a real Rube Goldberg construction, a decent into over-flowering needless complexity that doesn't solve anything real outside its own intricacies.

What was the original real problem, if any?

Upvotes: 0

Related Questions