Is there a way to make passing by reference, and passing by value explicit in the function call?

前端 未结 7 1556
北荒
北荒 2021-02-18 13:43

If you were to look at this code,

int x = 0;
function(x);
std::cout << x << \'\\n\';

you would not be able to verify through any me

7条回答
  •  Happy的楠姐
    2021-02-18 14:11

    This is C++: the lack of in and out parameters doesn't mean the language is deficient, it means you need to implement what other languages would do as a language feature as a library.

    Create two template classes and functions.

    in_param is a wrapper around a T const&, whilie io_param is a wrapper around a T& reference. You construct them by calling helper functions in and io.

    Inside, they behave like references (via overloading).

    Outside, the caller must call in or io on the argument, marking it up at the call site.

    out is trickier: inside the fumction, only assignment is legal. Ideally we would not even construct it: an emplace method might help.

    However, the caller needs some channel to know if the parameter was constructed or not.

    What I would do is out_param only has operator=, and it assigns. out wraps something into an out_param. If you want delayed constructuon, use optional inside the out param, which gets close. Maybe out_param also has emplace, which usually just assigns, but if the tyoe wrapped has emplace calls it instead?

    template
    struct in_param : std::reference_wrapper {
      explicit in_param( T const& t ):std::reference_wrapper(t) {}
      in_param( in_param&& o ):std::reference_wrapper(std::move(o)) {}
      void operator=( in_param const& o ) = delete;
    };
    template
    struct io_param : std::reference_wrapper {
      explicit io_param( T& t ):std::reference_wrapper(t) {}
      io_param( io_param&& o ):std::reference_wrapper(std::move(o)) {}
    };
    template
    in_param< T > in( T const& t ) { return in_param(t); }
    template
    io_param< T > io( T& t ) { return io_param(t); }
    
    template
    struct out_param {
    private:
      T& t;
    public:
      out_param( T& t_ ):t(t_) {}
      out_param( out_param&& o ):t(o.t) {}
      void operator=( out_param const& o ) = delete;
      void operator=( out_param && o ) = delete;
      void operator=( out_param & o ) = delete;
      void operator=( out_param && o ) = delete;
      template
      out_param& operator=( U&& u ) {
        t = std::forward(u);
        return *this;
      }
      // to improve, test if `t` has an `emplace` method.  If it does not,
      // instead do t = T( std::forward(us)... ). (I'd use tag dispatching
      // to call one of two methods)
      template
      void emplace( Us&&... us ) {
        t.emplace( std::forward(us)... );
      }
    };
    template
    out_param out( T& t ) { return out_param(t); }
    

    or something like the above.

    You now get syntax like:

    void do_stuff( int x, in_param y, io_param z, out_param d );
    
    int main() {
      expensive a;
      something b;
      double d;
      do_stuff( 7, in(a), io(b), out(d) );
    }
    

    and failure to call in, io or out at the call site results in compile time errors. Plus, out_param makes it quite difficult to accidentally read the state of the out variable within the function, producing some very nice documentation at the call site.

提交回复
热议问题