Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should methods returning const std::string& return const std::string_view instead?

Assume we have a simple getter method in a class that returns a const reference to a std::string member:

const std::string& getString() const noexcept { return someString; }

With the advent of std::string_view in C++17, I wonder whether it has any advantages of writing this instead:

const std::string_view getString() const noexcept { return someString; }

Does one method have advantages/disadvantages over the other? Clearly (correct me if I'm wrong) both solutions will definitely be better than this:

const char* getString() const noexcept { return someString.c_str(); }

I've seen this related question, but I'm asking for something slightly different.

like image 494
andreee Avatar asked Jun 14 '19 15:06

andreee


People also ask

When should I return my const?

Thus, it's important to use const when returning an object by value if you want to prevent its use as an lvalue. The reason const has no meaning when you're returning a built-in type by value is that the compiler already prevents it from being an lvalue (because it's always a value, and not a variable).

What is const std :: string?

The data type const string& literally means “a reference to a string object whose contents will not be changed.” There are three ways to pass things around (into and out of functions) in C++: 1. Pass by value - a copy of the original object is created and passed.

Should you pass string by reference?

I believe the normal answer is that it should be passed by value if you need to make a copy of it in your function. Pass it by const reference otherwise.

Can a const method return a non const reference?

For safety and simplicity, don't return pointers or references to non-const objects from const methods. Within that constraint, mark methods as const where possible. Avoid const_cast to remove const, except when implementing non-const getters in terms of const getters.


1 Answers

Yes, you should write:

const std::string& getString() const noexcept { return someString; }

Instead of (note: not const, because never return const values):

std::string_view getString() const noexcept { return someString; }

The reason is - you already have a string. So it's not like you have to pay anything extra to get a string out of it. And string has one notable semantic difference to an arbitrary string_view: it's null-terminated by guarantee. We know this. Maybe some downstream user needs to rely on that information. If they need null-termination (e.g. they need to pass to some C API that requires it) and you give a string_view, they have to make a string out of it themselves. You save nothing, but potentially make downstream users do more work.

If, however, you had a vector<char> instead... then I would suggest to return a span<char const> or the equivalent thereof. Since there is no semantic difference and you're just providing a view.


There also the separate argument of what:

auto x = obj.getString();

should do. This either takes a copy of the string (expensive, but safe) or effectively a reference to it (cheap, but potentially dangling). But it doesn't entirely look like a reference, it looks like a value. This is a broad issue with reference-semantic types in general (things like reference_wrapper, string_view, span, tuple<T&...>, optional<T&> if it existed, etc.).

I don't have an answer for this case, but it's something to be aware of.

like image 89
Barry Avatar answered Oct 18 '22 05:10

Barry