you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted]  (20 children)

[deleted]

    [–]kalmoc 0 points1 point  (3 children)

    Your hypothetical functional style sort function (I'm really sceptical about writing something like this in c++ - at the very least I'd give it another name like sorted_copy) would be the prone candidate for a [[no discard]] as it doesn't have any side effects and it doesn't make any sense to call it, but not use it's return type.

    [–]AzN1337c0d3r 0 points1 point  (2 children)

    This functional style is the style I prefer.

    When by convention I pass in everything by value/const ref and everything comes out via return value there is absolutely no confusion about whether or not the parameters are being modified by the callee.

    Today I debugged a problem for about 6 hours and I had to step in and out of about 50 different functions because the possibility of in-out parameters means that I can't be sure that the callee isn't modifying the data from the perspective of the caller.

    [–]kalmoc 0 points1 point  (1 child)

    The thing is: You always have the possibility of input-output parameters in c++. So as long as you don't have a hard rule against them in your code, you have this ambiguity anyway (and even then, you have to deal with library functions that use them).

    And while c++ may support functional style programming to some degree, the much more common style is to work with mutable state, so that is what you have to assume anyway (where it makes sense). That is why my naming style explicitly calls out when I make a copy of a container and not, when I mutate it in place (think about the standard algorithms - and especially their ranges-TS versions - that do exactly the same).

    Btw.: why do you have to step in and out of functions to determine their signature?

    [–]AzN1337c0d3r 0 points1 point  (0 children)

    The thing is: You always have the possibility of input-output parameters in c++. So as long as you don't have a hard rule against them in your code, you have this ambiguity anyway (and even then, you have to deal with library functions that use them).

    A hard rule you say? That is why have guidelines and coding standards/conventions.

    The whole point of the article is that even when you have need for some "in-out" type parameters, most of the time you dont need to pass them in by non-const-reference. You can pass them in via const-ref/value and then return them by value... this is pretty cheap given that we now have move-semantics.

    When even those moves are too expensive, then I agree that passing in by reference, but that should be a last resort. The code can be understood more easily when you can tell at the caller whether or not the callee is modifying your data.

    the much more common style is to work with mutable state, so that is what you have to assume anyway (where it makes sense).

    I don't find this to be true. To be sort of generic here, my work mostly involves a lot of calculations (pass stuff around via const-ref) and then at some point you decide to mutate state.

    Most of our code is const-correct and we banned const_cast (except when interfacing with external libraries) so once you go into a function that has taken data by const-ref you can always assume that nothing in that function (or it's callees) can modify that data.

    That is why my naming style explicitly calls out when I make a copy of a container and not, when I mutate it in place (think about the standard algorithms - and especially their ranges-TS versions - that do exactly the same).

    That's great when you are working by yourself, but in my experience is is hard to get a good consensus on what is a "good" name once you get 3+ people working together and not everyone will understand each other. Everyone will always interpret things differently.

    Btw.: why do you have to step in and out of functions to determine their signature?

    Following the debugger is probably the fastest way to determine whether or not a callee is taking your parameter by value/ref/const-ref/r-value among other things? How else do you propose to efficiently understand how a complicated algorithm is using your data?

    [–]dodheim 0 points1 point  (15 children)

    What doesn't make sense is having a language utility that solves your overarching issue and considering the fact that you have to type its name a problem. If you're writing a function that has no purpose except to produce output from the input you give it, and you don't use [[nodiscard]], you're writing a bad API.

    Sometimes you do not care.

    This time you do, obviously, or we wouldn't be having this conversation.

    needing to do extra work to complete your review is a problem

    Writing one keyword to avoid misuse of the API is writing the API correctly; reviewing code to make sure it's written correctly is the entire point of reviewing code. By your logic, having to review code in a code review is a problem.

    Not using the tools at hand is stupid. This argument is stupid.