mardi 2 juin 2015

Calling const mutable lambdas

To simplify the testcase, suppose that I have the following wrapper class:

template <typename T>
struct Wrapper {
  decltype(auto) operator()() const {
    return m_t();    // <-- This is wrong
  }
  decltype(auto) operator()() {
    return m_t();
  }
  T m_t;
};

template <typename T>
auto make_wrapper(T t) {
  return Wrapper<T>{t};
}

And let’s say I am wrapping the following trivial functor returning references:

struct Foo {
  int& operator()() {
    return x;
  }
  const int& operator()() const {
    return x;
  }
  int x;
};

In my main function, I am trying to wrap the Foo functor into a lambda closure. Since I want it to return non-const references, I am setting it mutable and using decltype(auto):

int main() {
  Foo foo;
  auto fun = [foo]() mutable -> decltype(auto) { return foo(); };
  auto wfun = make_wrapper(fun);
  const auto& cwfun = wfun;

  wfun();     // <- OK
  cwfun();    // <- BAD!
}

For the second call, cwfun(), the first const version of Wrapper::operator() is called, but there m_t is then viewed as a const lambda, and thus cannot be called. I suppose this is because m_t was marked mutable in the first place. So what would be a good way of making that work? Convert m_t to a non-const before calling it in operator() const?

The error message given by clang looks like:

tc-refptr.cc:8:12: error: no matching function for call to object of type 'const (lambda at
      tc-refptr.cc:40:14)'
    return m_t();
           ^~~
tc-refptr.cc:44:27: note: in instantiation of member function 'Wrapper<(lambda at
      tc-refptr.cc:40:14)>::operator()' requested here
  DebugType<decltype(cwfun())> df;
                          ^
tc-refptr.cc:40:14: note: candidate function not viable: 'this' argument has type 'const
      (lambda at tc-refptr.cc:40:14)', but method is not marked const
  auto fun = [foo]() mutable -> decltype(auto) { return foo(); };

Aucun commentaire:

Enregistrer un commentaire