7

(The root problem here is that I'm trying to use decltype, or some other type deduction perhaps based on auto, on complicated expressions that involve lambdas. I'm trying to find some sort of workaround. I've been playing around with http://pfultz2.github.com/Pythy/ for polymorphic lambdas. I can't fully go into the motivation without telling you all a long story! )

I want to be able to do decltype([](int){return 3.5L}; to get the type of the lambda, or at least the return type. Yes, I know that lambdas are given a unique type, I don't need to be reminded that decltype([](int){return 3.5L}; will give two different types if used on two different lines.

If I use decltype on a lambda, then I get an error message ('lambda used in unevaluated context'). I know that seems like a reasonable error message, but I'm surprised at C++ holding my hand like that! It would be useful to be allowed to do this, in particular to access the return type of the lambda. Is this error simply a result of overzealous error messages, or is there truly a good reason why it can't be done?

An expression such as this works inside a member function:

template<typename T>
struct X {
    void foo() {
        static auto l = [](int){return 3.5;};
    }
};

but I'm not allowed to do:

template<typename T>
struct X {
    static auto var = [](int){return 3.5;}; // will also fail if I use constexpr here
};

x.cpp:8:47: error: expression ‘#‘lambda_expr’ not supported by
    dump_expr#<expression error>’ is not a constant-expression
x.cpp:8:47: error: unable to deduce ‘const auto’ from ‘<expression error>’

This inspired my idea to try to use a static variable in a function in order to do the type inference on the lambda.

This seems to work a little better if X is not a template. But I need X to be a template - in particular, the arguments to the lambda will take the type of the template parameters.

Remember, I only want the type of the lambda, and I would be satisfied only with the return type. It's frustrating that the compiler is willing and able to do type inference, and static initialization, in both cases, but it seems to put some arbitrary obstacles in my way.

Can I access the type of the variable var_in_func from outside the dummy_func function?

struct S {
    constexpr static auto member = a_complicated_expression...  // error
    void dummy_func() {
        static auto var_in_func = a_complicated_expression...    // OK
    }
    typedef dummy_func :: var_in_func the_type; // I'd like this to work
};

If there are lambdas in a_complicated_expression..., there is often a problem with the initializer for member. If S is actually a struct template, then I get error messages that member has no initializer. That's why I'm trying to find other ways around this.

However, the static auto variable inside the static method dummy_func works fine. So that got me thinking that they ought to be a nice way to access the type of that static variable?

I tried the following but it didn't work because dummy_func isn't a type (fair enough):

typedef dummy_fun :: var_in_func the_type_of_the_static_variable_in_the_method;

I can't do decltype( a_complicated_expression... ) as the compiler complains about the use of a lambda in an unevaluated context (a declspec).

I'm using g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3. I don't mind if I have to use g++ specific extensions.

Aaron McDaid
  • 26,501
  • 9
  • 66
  • 88
  • Have you tried `decltype ` ? (not sure what that `declspec` is) – J.N. Sep 04 '12 at 02:22
  • I might be wrong, but I have a feeling that it's hopeless to try and access the type defined for a static function-local variable from outside that function. Would it be worthwile trying to find out why the direct declaration of `member` does not work? What is the nature of that _complicated-expression_? Perhaps those problems can actually be resolved. – jogojapan Sep 04 '12 at 06:54
  • The complicated expression involves a lambda, but decltype refuses to work with lambdas ('error: lambda used in unevualated context'). I'll edit the question. – Aaron McDaid Sep 04 '12 at 07:53
  • @J.N. that was just a typo by me. I meant `decltype`, not `declspec`. – Aaron McDaid Sep 04 '12 at 08:05
  • I am already too far away from current-day C++, but maybe http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda could help you? – gimpf Sep 04 '12 at 10:34
  • Is the larger problem you are trying to solve that of having a static class member that is a lambda function? If this is the case why can't you use just use a normal static class function? – Andrew Tomazos Sep 04 '12 at 11:54
  • Also do lambda types define an inner type called "result_type", I think std::bind expressions and std::functions do at least. – Andrew Tomazos Sep 04 '12 at 11:56
  • @AndrewTomazos-Fathomling, the Pythy library that I mentioned attempts to make polymorphics lambdas. In theory that are not possible, but it appears we can get very close. I've been playing around with simple versions of that code and attempting to come up with a more reliable version. It's really frustrating that one can't put a lambda inside `decltype`. I believe it should be allowed, even if only to get the return type of the lambda. – Aaron McDaid Sep 04 '12 at 12:50
  • you are not going to be able to use your class in more than one translation unit because the lambda will always have a different type. Pretty useless I say :) – Johannes Schaub - litb Sep 06 '12 at 21:14
  • 1
    ... but useful enough for Dave Abrahams, of the C++ Standards Committee, to write a [blog post](http://cpp-next.com/archive/2011/11/having-it-all-pythy-syntax/) about polymorphic lambdas **:-)** – Aaron McDaid Sep 07 '12 at 10:21

2 Answers2

1

Non-capturing lambdas can be converted to a function pointer, and have an operator() with the signature of that function pointer:

template<typename C, typename R, typename... Args>
auto remove_class(R (C::*)(Args...)) -> R(*)(Args...);
template<typename C, typename R, typename... Args>
auto remove_class(R (C::*)(Args...) const) -> R(*)(Args...);
template<typename C, typename R, typename... Args>
auto remove_class(R (C::*)(Args...) volatile) -> R(*)(Args...);
template<typename C, typename R, typename... Args>
auto remove_class(R (C::*)(Args...) const volatile) -> R(*)(Args...);

template<typename T>
auto to_f_ptr(T t) -> decltype(remove_class(&T::operator())) { return t; }

You can now write auto var = to_f_ptr([](int){return 3.5;}); and var will have type double (*)(int).

However, you still won't be able to use the lambda as a class-scope static initialiser; see lambda as a static member.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
  • I know that already. But it still doesn't change the principle that, in my opinion, it should be possible to get the return type of the lambda. Maybe something like `decltype([](int){...}) :: return_type`. – Aaron McDaid Sep 04 '12 at 12:45
  • .. I don't need to be able to initialize the value of the static member, I just want to be able to define the type correctly. The Pythy library I references attempts to set up a null pointer of type `decltype(..theLambda..)*`. – Aaron McDaid Sep 04 '12 at 12:47
  • 1
    @AaronMcDaid you're out of luck; 5.1.2:2 says *A* lambda-expression *shall not appear in an unevaluated operand*. – ecatmur Sep 04 '12 at 12:55
  • I'm going to recompile gcc without that error message. Wish me luck! A lambda expression definitely has a type, so `decltype` should work on it. We can debate the usefulness of such a type later, but in principle I don't see why the standard ties our hands like that. – Aaron McDaid Sep 04 '12 at 13:02
  • I removed that error message from g++ and I managed to solve my original problem. Now it will accept lambdas inside a `decltype` (actually, I used `typeof`, a g++ extension). I've [http://www.aaronmcdaid.com/2012/09/typeof-or-c-please-stop-holding-my-hand.html blogged about it]. It can be useful to do decltype on lambdas, even though I understand why, at first glance, it seems useless. – Aaron McDaid Sep 05 '12 at 19:13
  • The discussion at http://compgroups.net/comp.lang.c++.moderated/rationale-for-lambda-expressions-not/223030 indicates a fear that decltype-lambdas would be used in SFINAE, requiring the compiler to implement a "sandbox" for any SFINAE context. Note that your blog post already uses the gcc (and MSVC, etc.) extension of allowing return-type-deduction for multiple-statement lambdas, currently http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#975 - although if gcc can support this use then that would be an argument for the standard to accept it. – ecatmur Sep 05 '12 at 19:22
  • @AaronMcDaid Also, in case I wasn't sufficiently clear above, ISTM decltype-lambdas can *only* be useful with multiple-statement-lambda return-type-deduction, which is as of yet non-standard. – ecatmur Sep 05 '12 at 19:24
  • (What does ISTM mean?) Anyway, thanks, I didn't realize that multi-statement lambdas were an extension. I suppose this is all a bit complex. – Aaron McDaid Sep 05 '12 at 21:04
  • @AaronMcDaid "it seems to me". – ecatmur Sep 05 '12 at 21:23
0

A lambda function is defined only in the the context of its scope. This implies that the lambda function within the static member function is of different type than that defined elsewhere. It seems to me that obtaining the return-type of a lambda function outside of its scope is not sensible and shouldn't be possible.

There must be another way (not involving a lambda) to get the type you need, since it apparently makes sense to define such a type outside of the scope of any potential lambda function.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • 1
    "This implies that the lambda function within the static member function is of different type than that defined elsewhere" I have made clear that I understand this already! Anyway, the return type will be the same across two lambdas that have the same code, so it does make sense for me to want to take the return type of a lambda, even if I never use that lambda. – Aaron McDaid Sep 04 '12 at 12:44