1

Say I have a C++ function

/**
 * @param path If empty, the system default is used
 */
void foo(const std::string& path);

And in my implementation I have a default handling for empty paths

void foo(const std::string& path) {
    if (path.empty()) {
        // Use some default
    } else {
        // Use the given path
    }
}

However, the if-else blocks are quite the same so I would either write

A

void foo(const std::string& path_) {
    const std::string& path = path_.empty() ? "some default" : path_;
    // Use the given path
}

B

or change the function definition altogether:

void foo(std::string path) {
    if (path.empty() {
        path = "some default";
    }
    // Use the given path
}

Now I am wondering what is advised in terms of style in such cases:

  • A seems a little odd at first because I explicitly made the parameter const and now start to somehow "rewrite" it. Would this code look like overkill?
  • B seems a little odd because then, this function would have a different signature just because of some implementation details. It would stand out from the other functions which still use const&
2
  • 2
    Hi. This question should rather go on SO. However, you should never change the signature (i.e. interface) to satisfy some internal implementation details.
    – Christophe
    Commented Jan 17, 2020 at 11:34
  • @Christophe That's too absolute. Yes, you should generally prefer ease-of-use over ease-of-implementation, but choosing the right way to balance and reconcile them is an art, and includes selecting the signature and contract. Commented Jan 17, 2020 at 17:24

2 Answers 2

4

Unless there is a good reason not to (meaning your string needs the terminator), I would prefer changing the signature:

void foo(std::string_view path)

It potentially increases efficiency for the caller and the callee.

And doing

if (path.empty())
    path = "some default";

is thereafter efficient and the obvious thing to do.

Go for a good interface first, a good implementation second. Adjusting the interface can be beneficial for efficiency, but be careful not to let too many implementation-details bleed into the interface, nor to compromise usability more than warranted.

As an aside, there is a third option you didn't mention, namely tail-recursion:

void foo(const std::string& path) {
    const static std::string default_path = "some_default";
    if (path.empty())
        return foo(defauult_path);
    // Use the given path
}
2
  • 1
    I understand that you're talking about rvalue optimizarion in your first example? Just be careful that you need "modern" c++ for it to works. Some unfortunate souls still work with c++98
    – JayZ
    Commented Jan 17, 2020 at 13:50
  • @JayZ No, the first example only needs the C++17 reference-type std::string_view. There are backports to at least C++11, I don't think going further down is too hard. Commented Jan 18, 2020 at 13:22
1

Yes, it is a bit weird to change the signature of a function because of the implementation. I would prefer option A because of the mentioned reason and because it is more restrictive.

Assuming the function parameter is used to open a file at a given path, it makes a lot of sense to declare it const. But I also see no reason to use pass-by-reference using the reference symbol '&' when the parameter is already declared const. If you, for example, were to use the parameter to open a file, you just need the value. If you want to change the value of the parameter, then it makes sense, but then it should not be declared const.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.