1

I'm trying to understand why this snippet fails:

#include <iostream>

using namespace std;


template <typename Lambda>
struct Handler
{
  bool _isCompleted;

  bool isCompleted() { return _isCompleted; }

  Lambda _l;
  Handler(Lambda&& l) : _l(l) {}

  void call() { _l(this); }
};

int main()
{
  auto l1 = new Handler( [&](decltype(l1) obj )->
{
  obj->_isCompleted = true;
  cout << " is completed?" << obj->isCompleted() << endl;
});
  l1->call();
};

g++ 4.5 fails with:

test.cpp: In function ‘int main()’:
test.cpp:21:17: error: expected type-specifier before ‘Handler’
test.cpp:21:17: error: expected ‘,’ or ‘;’ before ‘Handler’
test.cpp:25:2: error: expected primary-expression before ‘)’ token
test.cpp:25:2: error: expected ‘;’ before ‘)’ token
test.cpp:26:7: error: request for member ‘call’ in ‘* l1’, which is of non-class type ‘int’

my understanding is that auto l1 should resolve to Handler<lambdaType>* and lambdaType should have a public function signature void( Handler<LambdaType>*). I don't see any blatantly wrong with the above example (you know, besides the ugliness and the slightly pathological cyclic dependency between the lambda and the handler type)

4

2 回答 2

5

One problem is, as @Cat said, that template argument deduction just does not work for constructor calls. You always need to specify the template argument.

The other problem is nicely illustrated by Clang with the following snippet:

struct X{
  X(...){}
};

int main(){
  auto l = X([](decltype(l)& o){});
}

Output:

t.cpp:6:26: error: variable 'l' declared with 'auto' type cannot appear in its
      own initializer
  auto l = X([](decltype(l)& o){});
                         ^
1 error generated.

Obligatory standard quote:

§7.1.6.4 [dcl.spec.auto] p3

Otherwise, the type of the variable is deduced from its initializer. The name of the variable being declared shall not appear in the initializer expression. [...]

于 2012-05-21T17:42:18.110 回答
2

Type deduction doesn't work for constructors. auto will deduce the type for the expression, yes, but new Handler() requires explicit type. Write a factory function instead:

// also don't use raw owning pointers
template <typename L>
std::unique_ptr<Handler<L>> make_handler(L lambda) {
    return std::unique_ptr<Handler<L>>(new Handler<L>(lambda));
}

Sure, it's repeating yourself a bit, but only once. Then you can do

auto l1 = make_handler([](...) { ... });
auto l2 = make_handler([](...) { ... });

and it'll work fine.

于 2012-05-21T17:36:06.960 回答