Are there any downsides to marking all variables you don't modify const?

前端 未结 6 2070
清歌不尽
清歌不尽 2021-01-07 16:24

After much googling I\'ve found a lot about marking functions and their parameters as const, but no guide on marking variables as const.

He

6条回答
  •  爱一瞬间的悲伤
    2021-01-07 16:41

    Instead of this ¹non-standard code:

    #import 
    #import 
    
    void example(const std::string& x) {
      size_t length = x.length();
      for (size_t i = 0; i < length; ++i) {
        std::cout << x.at(i) << std::endl;
      }
    }
    
    int main() {
      example("hello");
    }
    

    … I'd write this:

    #include 
    #include 
    using namespace std;
    
    void example( string const& s )
    {
        for( char const ch : s )
        {
            cout << ch << '\n';
        }
    }
    
    auto main()
        -> int
    { example( "hello" ); }
    

    The main place I could add const, relative to the original code, was for the ch variable in the loop. I think that's nice. const is generally desirable because it reduces the possible code actions one has to consider, and range based loops let you have more const.

    The main drawback of using const for most things, is when you have to relate to C APIs.

    Then one just has to make some gut feeling decisions about whether to copy data, or trust in the documentation and use a const_cast.


            Addendum 1:
    Do note that const on a return type prevents move semantics. As far as I know this was first noted by Andrei Alexandrescu in his Mojo (C++03 move semantics) article in Dr Dobbs Journal:

    [A] const temporary looks like an oxymoron, a contradiction in terms. Seen from a practical perspective, const temporaries force copying at destination.

    So, this is one place where one should not use const.

    Sorry that I forgot to mention this originally; I was reminded by user bogdan's comment on another answer.


            Addendum 2:
    In the same vein (to support move semantics), if the last thing done with a formal argument is to store a copy somewhere, then instead of passing by reference to const it can be better to use a non-const argument passed by value, because it can be simply moved from.

    I.e., instead of

    string stored_value;
    
    void foo( string const& s )
    {
        some_action( s );
        stored_value = s;
    }
    

    … or the redundancy of optimized

    string stored_value;
    
    void foo( string const& s )
    {
        some_action( s );
        stored_value = s;
    }
    
    void foo( string&& s )
    {
        some_action( s );
        stored_value = move( s );
    }
    

    … consider just writing

    string stored_value;
    
    void foo( string s )
    {
        some_action( s );
        stored_value = move( s );
    }
    

    It can be slightly less efficient for the case of lvalue actual argument, it discards the advantages of const (constraints on what the code could possibly do), and it breaks a uniform convention of using const wherever possible, but it doesn't perform badly in any situation (which is the main goal, to avoid that) and it's smaller and possibly more clear code.


    Notes:
    ¹ Standard C++ does not have an #import directive. Also, those headers, when properly included, are not guaranteed to define size_t in the global namespace.

提交回复
热议问题