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

前端 未结 7 1510
北荒
北荒 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条回答
  • 2021-02-18 13:58

    Many (most) IDE's help you with this problem by displaying the function/method prototype(s) once they figure out which function you are calling.

    0 讨论(0)
  • 2021-02-18 14:03

    Some people insist that the correct way to pass mutable object is to use a pointer. That is, you would pass

    Lowercase(&str);
    

    ... and Lowercase() would, obviously, be implemented to take a pointer. That approach may suit your needs.

    I want to mention, however, that this is not what I would do! Instead, the approach I favor is to use appropriate names instead. For example,

    inplace_lowercase(str);
    

    pretty much says what it is going to do. Clearly, inplace_lowercase() would actually be an algorithm and with a bit of magic could be reasonably be called as

    inplace_lowercase(str.begin() + 1, str.end());
    

    as well.

    Here are a few reasons why I don't like passing arguments by pointer and/or why I don't believe in an explicit indication of how the argument is passed:

    • Pointers can be null. A mandated reference parameters should, in my opinion, be a reference.
    • Passing by pointer still doesn't indicate whether the argument may be modified are not as the argument may be a T const*.
    • Having meaningful names makes it actually easier to understand what's going on in the first place.
    • Calling something without consulting its documentation and/or knowing what the called function will do doesn't work anyway and indicating how things are passed is trying to cure symptoms of a deeper problem.
    0 讨论(0)
  • 2021-02-18 14:10

    If you use MS VC++ then maybe it will be useful information about source-code annotation language (SAL) http://msdn.microsoft.com/ru-ru/library/hh916383.aspx

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

    or something like the above.

    You now get syntax like:

    void do_stuff( int x, in_param<expensive> y, io_param<something> z, out_param<double> 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.

    0 讨论(0)
  • 2021-02-18 14:11

    I think it's something useless to notify (by language nonetheless [1]). The only needed question is : "Is my object is semantically modified ?", and so :

    • When you read a prototype you know if a function could modify an object (non-const ref) or not (copy or const ref).
    • When you use a function (even without reading [2] the prototype) if you have to be sure to not modify an object, use a const_cast.

    [1] A static analyzer could do it for its purposes.
    [2] If you miss, the compiler would warn you anyway.

    0 讨论(0)
  • 2021-02-18 14:23

    I'm not sure I understand your requirements completely, but maybe this is something you can use:

    template<typename T>
    void foo( T ) { static_assert( sizeof(T)==0, "foo() requires a std::ref" ); }
    
    void foo( std::reference_wrapper<int> t )
    {
        // modify i here via t.get() or other means of std::reference_wrapper
    }
    
    int main()
    {
        int i = 42;
        // foo( i ); // does not compile, static_assert fires
        foo( std::ref( i ) ); // explicit std::ref visible on the caller's side
    }
    
    0 讨论(0)
提交回复
热议问题