Performance comparison: f(std::string&&) vs f(T&&)

fast string comparison c
c string::compare
string literal std
c++ std::string suffix
c++ literal std string
string comparison optimization c
python string comparison efficiency

I'm trying to understand the performance implications of using WidgetURef::setName (URef being a Universal Reference, the term coined by Scott Meyers) vs WidgedRRef::setName (RRef being an R-value Reference):

#include <string>

class WidgetURef {
    public:
    template<typename T>
    void setName(T&& newName)
    {
        name = std::move(newName);
    }
    private:
        std::string name;
};

class WidgetRRef {
    public:
    void setName(std::string&& newName)
    {
        name = std::move(newName);
    }
    private:
        std::string name;
};

int main() {
    WidgetURef w_uref;
    w_uref.setName("Adela Novak");

    WidgetRRef w_rref;
    w_rref.setName("Adela Novak");
}

I do appreciate that with universal references one should be using std::forward instead, but this is just an (imperfect) example to highlight the interesting bit.

Question In this particular example, what is the performance implications of using one implementation vs the other? Although WidgetURef requires type deduction, it's otherwise identical to WidgetRRef, isn't it? At least in this particular scenario, in both cases the argument is an r-value reference, so no temporaries are created. Is this reasoning correct?

Context The example was taken from Item25 of Scott Meyers' "Effective Modern C++" (p. 170). According to the book (provided that my understanding is correct!), the version taking a universal reference T&& doesn't require temporary objects and the other one, taking std::string&&, does. I don't really see why.

In this particular example, what is the performance implications of using one implementation vs the other?

Universal references, as Scott Meyers calls them, are not primarily there for performance reasons, but, loosely speaking, to treat both L- and Rvalue references in the same manner to avoid countless overloads (and for being able to propagate all type information during forwarding).

[...] so no temporaries are created. Is this reasoning correct?

Rvalue references do not prevent temporaries from being created. Rvalue references are the kind of references that are able to be bound to temporaries (apart from const lvalue references)! Of course, in your example, there will be temporaries, but the rvalue reference can bind to it. The universal reference first has to undergo the reference collapsing but in the end, the behaviour will be identical in your case:

// explicitly created temporary
w_uref.setName(std::string("Adela Novak")); 
// will create temporary of std::string --> uref collapses to rvalue ref
// so is effectively the same as
w_rref.setName("Adela Novak");

By using the rvalue reference on the other hand, you force a temporary implicitly as std::string&& cannot bind to that literal.

w_rref.setName("Adela Novak"); // need conversion

So the compiler will create a temporary std::string from the literal the rvalue reference then can bind to.

I don't really see why.

In this case, the template will be resolved to const char(&)[12] and thus, no std::string temporary will be created in contrast to the case above. This therefore is more efficient.

C++ String Compare Performance « A howl on the wind…, In this test, all comparisons are of not-equal strings. Idea from #C++ on QuakeNet​, where we always advocate using std::string over various  According to the book (provided that my understanding is correct!), the version taking a universal reference T&& doesn't require temporary objects and the other one, taking std::string&&, does. I don't really see why.

setName(T&& newName) with argument "Adela Novak" gets T duduced as const char (&)[12] which is then assigned to std::string.

setName(std::string&& newName) with argument "Adela Novak" creates a temporary std::string object which is then move assigned to std::string.

The first one is more efficient here because there is no moving involved.

Performance of std::string_view vs. std::string From C++17, How much is std::string_view faster than standard std::string operations? Have a look at a few examples where I compare std::string_view  Depending on the architecture the total size is 8 or 16 bytes. Due to Small String Optimizations, std::string is usually 24 or 32 bytes so doubles or triple the size of string_view.In that form

Scott himself says that WidgetURef "compiles, but is bad, bad, bad!" (verbatim). These two classes behave differently as you use std::move instead of std::forward: setName therefore can modify its argument:

#include <string>
#include <iostream>

class WidgetURef {
    public:
    template<typename T>
    void setName(T&& newName)
    {
        name = std::move(newName);
    }
    private:
        std::string name;
};

int main() {
    WidgetURef w_uref;
    std::string name = "Hello";
    w_uref.setName(name);
    std::cout << "name=" << name << "\n";
}

can easily print name=, meaning that the value of name was changed. And indeed it does on ideone at the very least.

On the other hand, WidgetRRef requires that the passed argument is a rvalue-reference, so the example above wouldn't compile without explicit setName(std::move(name)).

Neither WidgetURef, nor WidgetRRef require creating extra copies if you pass std::string as an argument. However, if you pass something which std::string can be assigned from (such as const char*), then the first example will pass that by reference and assign it to string (without any copies except for copying data from C-style string into std::string), and the second example will first create a temporary string, and then pass it as an rvalue reference to the method. These properties preserve if you replace std::move(newName) with a correct std::forward<T>(newName).

String Library Comparison Table, std::string, VStr, firestring, Safe C String Library, c2lib, TR 24731. ANSI C0x, Managed Strings ANSI C0x, MS SDK Strsafe.h, libclc. Speed on large strings A performance comparison of the speed of various ways to compare strings in C++. In this test, all comparisons are of not-equal strings. Idea from #C++ on QuakeNet, where we always advocate using std::string over various char* functions.

Assuming the arguments as stated in the question

template<typename T>
void setName(T&& newName)
{
    name = std::forward<T>(newName);
}

Will invoke the std::string assignment operator for the data member name with a const char * argument

void setName(std::string&& newName)
{
    name = std::move(newName);
}

Invokes std::string constructor to create a temporary, to which the Rvalue Ref can bind to.

Invokes std::string move assignment / constructor for the data member name with a std::string&& argument

Invokes std::string destructor to destroy the temporary, from which we moved the data.

better performance comparing std::string to std::string_view literals , better performance comparing std::string to std::string_view literals vs cstr literals enough to move the byte characters into a register for a string_view literal. Compare strings Compares the value of the string object (or a substring) to the sequence of characters specified by its arguments. The compared string is the value of the string object or -if the signature used has a pos and a len parameters- the substring that begins at its character in position pos and spans len characters.

4. Optimize String Use: A Case Study - Optimized C++ [Book], The C++ std::string class templates are among the most used features of the C++ Dynamic allocation is expensive compared to most other C++ features, so no It looks innocent enough, but the performance of the function as written is  Good info here already, but a few higher-level comments about this: * For many situations, the performance difference is not going to be big enough to worry about. So before rewriting code in a less readable and maintainable way, decide if this

Performance of std::string_view vs std::string , Have a look at a few examples where I compare std::string_view against std::​string . Intro; The Series  Laptop comparison: find the best laptop for your needs! Search our large database and compare laptops by price, specs, and features.

mloskot/string_benchmark: Simple benchmark comparing , Simple non-academic performance comparison of various operations on C++ strings: C strings, character arrays, std::string and std::wstring classes as well as  Is there any comparison data on perfomance difference between std::string and c style string? Or maybe if there are source code which could be used to measuer on different compiler/platform, in a

Comments
  • std::moveing from an universal reference doesn't do what you think it does. Use std::forward instead.
  • Why does Mr. Meyers think it's necessary to coin new terms when the ones in the standard are more than sufficient?
  • std::string is not the default type of a character array.
  • @Bathsheba I think he coined the term long before the standard did so.
  • There is more to this than parameter passing. You should also consider inlining, the use of small string optimization, and the actual cost of constructing a string. Often low-level optimization attempts turn out to be premature.
  • No, w_uref.setName("Adela Novak") won't create a temporary: it will pass C-style string as an argument and then call operator=(const char*)
  • @yeputons Never said that, note there is an explicit temporary to illustrate the fact that in this case, Uref and Rvalue ref behave the same.
  • Cheers @Jodocus, that's spot on! Much appreciated!
  • std::string&& inlines better. I doubt the moving have much to do with efficiency
  • @PasserBy Basically, you claim that char const(&)[12] -> string -> string is faster than char const(&)[12] -> string, is that right?
  • I meant after optimizations, it isn't the deciding factor. Especially considering string moves is likely copying 16 bytes. (It's not, wow that assembly is horrible)
  • Thank you @MaximEgorushkin, once you mentioned what T is being deduced as I immediately realised what was the mistake in my reasoning!
  • Correct usage of std::forward with universal references like T&& is std::forward<T>.