Search code examples
c++templatesc++23

How to make proxying const/ref/volatile qualification without duplication in C++?


Say I want to write some "proxy" object for a callable, e.g. to add some feature that happens before the wrapped callable is invoked. Further, say I want to be const-correct, so that the proxy has an accessible non-const operator() only if the wrapped callable does. Then I need to do something like this, defining two versions of the operator and using SFINAE:

template <typename F>
struct Proxy {
  F wrapped;
  
  template <typename = std::enable_if_t<std::is_invocable_v<F&>>>
  auto operator()();
  
  template <typename = std::enable_if_t<std::is_invocable_v<const F&>>>
  auto operator()() const;
};

This is annoying enough, but if I also want to proxy ref qualifications now I need for overloads, which is really getting excessive. Heaven help me if I also want to proxy volatile qualfiication.

Is there a shortcut to make this less awful? I seem to recall there was at least a feature proposed for addition to the standard.


Solution

  • The proposed feature I was thinking of was P0847R7 ("Deducing this"), which was accepted for C++23. There are some useful slides here. I don't have a compiler to check this with, but I believe my four operators could be written as one with something like the following:

    template <typename F>
    struct Proxy {
      F wrapped;
      
      template <typename Me>
          requires std::invocable<std::like_t<Me, F>>
      auto operator()(this Me&& me);
    };
    

    (The type requirement here uses the hypothetical like_t metafunction assumed by P0847R7. See this question.)