c++c++20

Select non-const method based on lamba signature with auto arguments


I have the following problem where an incorrect method is selected because of lambda's auto arguments.

See the following example:

template <class T, class FunctionT>
concept ValidNonConstSignature = requires(FunctionT exec_fn, T& value) { exec_fn(value); };

template <class T, class FunctionT>
concept ValidConstSignature = requires(FunctionT exec_fn, const T& value) { exec_fn(value); };

class Foo {
public:
  template <class FunctionT> requires ValidNonConstSignature<int, FunctionT>
  auto
  getBar(FunctionT callback) {
    return callback(m_abc);
  }

  template <class FunctionT> requires ValidConstSignature<int, FunctionT>
  auto
  getBar(FunctionT callback) const {
    return callback(m_abc);
  }

private:
  int m_abc {};
};

int main()
{
    Foo foo;
    
    foo.getBar([](int& abc){ return abc++; });
    foo.getBar([](const int& abc){ return abc++; });   // correct compiler error: increment of read-only reference ‘abc’
    
    foo.getBar([](auto& abc){ return abc++; });        // incorrect compiler error: increment of read-only reference ‘abc’
    foo.getBar([](const auto& abc){ return abc++; });  // correct compiler error: increment of read-only reference ‘abc’

    return 0;
}

Because of the existence of getBar(FunctionT callback) const, the foo.getBar([](auto& abc){ return abc++; }); fails to compile.

If the getBar(FunctionT callback) const method is removed, foo.getBar([](auto& abc){ return abc++; }); compiles just fine.

Is there a way to somehow enhance the concepts to correctly select the non-const method? My initial idea was to get argument types via another template from the passed callback, but the compiler complained about incomplete type when passed lamda with auto in it.


Solution

  • [](auto& abc) { return abc++; } is not SFINAE friendly, so instantiating it with [auto = int] would produce hard error and not a substitution failure.

    If you are ok to make your functions not SFINAE friendly neither, you can "delay" the check with static_assert:

    class Foo
    {
    public:
      template <class FunctionT> 
      auto getBar(FunctionT callback) {
        static_assert(ValidNonConstSignature<int, FunctionT>);
        return callback(m_abc);
      }
    
      template <class FunctionT>
      auto getBar(FunctionT callback) const {
        static_assert(ValidConstSignature<int, FunctionT>);
        return callback(m_abc);
      }
    
    private:
      int m_abc {};
    };
    

    Demo