DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • DataWeave: Play With Dates (Part 1)
  • Tired of Messy Code? Master the Art of Writing Clean Codebases
  • The Long Road to Java Virtual Threads
  • Exploring Exciting New Features in Java 17 With Examples

Trending

  • A Deep Dive Into Firmware Over the Air for IoT Devices
  • Top Book Picks for Site Reliability Engineers
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 1
  • Building Enterprise-Ready Landing Zones: Beyond the Initial Setup
  1. DZone
  2. Data Engineering
  3. Data
  4. How to Initialize a String Member

How to Initialize a String Member

There are several ways to initialize a string member when working in C++. What way works best for your projects? Read on to find out!

By 
Bartłomiej Filipek user avatar
Bartłomiej Filipek
·
Aug. 15, 18 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
11.0K Views

Join the DZone community and get the full member experience.

Join For Free

How do you initialize a string member in the constructor? By using const string&, string value and move, string_view or maybe something else?


The article comes from bfilipek.com - blog about C++, new articles each Monday.


Let's have a look at possible options.

Intro

Below there's a simple class with one string member. We'd like to initialize it.

For example:

class UserName {
    std::string mName;

public:
    UserName(const std::string& str) : mName(str) { }
};

As you can see a constructor is taking const std::string& str.

You could potentially replace a constant reference with string_view:

UserName(std::string_view sv) : mName(sv) { }

And also you can pass a string by value and move from it:

UserName(std::string s) : mName(std::move(s)) { }

Which one alternative is better?

Analyzing the Cases

Let's now compare those alternative string passing methods in three cases: creating from a string literal, creating from lvalue, and creating from an rvalue reference:

// creation from a string literal
UserName u1{"John With Very Long Name"};

// creation from l-value:
std::string s1 { "Marc With Very Long Name"};
UserName u2 { s1 };

// from r-value reference
std::string s2 { "Marc With Very Long Name"};
UserName u3 { std::move(s2) };

And now we can analyze each version - with a string reference, a string_view, or a value. Please note that allocations/creation of s1 and s2 are not taken into account, we only look at what happens for the constructor call.

For const std::string&:

  • u1 - two allocations: the first one creates a temp string and binds it to the input parameter, and then there's a copy into mName.
  • u2 - one allocation: we have a no-cost binding to the reference, and then there's a copy into the member variable.
  • u3 - one allocation: we have a no-cost binding to the reference, and then there's a copy into the member variable.
  • You'd have to write a ctor taking r-value reference to skip one allocation for the u1 case, and also that could skip one copy for the u3 case (since we could move from r-value reference).

For std::string_view:

  • u1 - one allocation - no copy/allocation for the input parameter, there's only one allocation when mName is created.
  • u2 - one allocation - there's cheap creation of a string_view for the argument, and then there's a copy into the member variable.
  • u3 - one allocation - there's cheap creation of a string_view for the argument, and then there's a copy into the member variable.
  • You'd also have to write a constructor taking r-value reference if you want to save one allocation in the u3 case, as you could move from r-value reference.
  • You also have to pay attention to dangling string_views — if the passed string_view points to a deleted string object...

For std::string:

  • u1 - one allocation - for the input argument and then one move into the mName. It's better than with const std::string& where we got two memory allocations in that case. And similar to the string_view approach.
  • u2 - one allocation - we have to copy the value into the argument, and then we can move from it.
  • u3 - no allocations, only two move operations - that's better than with string_view and const string&!

When you pass std::string by value not only the code is simpler, there's also no need to write separate overloads for r-value references.

The approach of passing by value is consistent with item 41 - "Consider pass by value for copyable parameters that are cheap to move and always copied" from Effective Modern C++ by Scott Meyers.

However, is std::string cheap to move?

When String Is Short

Although the C++ Standard doesn't specify that, usually, strings are implemented with Small String Optimization (SSO); the string object contains extra space (in total it might be 24 or 32 bytes), and it can fit 15 or 22 characters without additional memory allocation. That means that moving such a string is the same as copying. And since the string is short, the copy is also fast.

Let's reconsider our example of passing by value when the string is short:

UserName u1{"John"}; // fits in SSO buffer

std::string s1 { "Marc"}; // fits in SSO buffer
UserName u2 { s1 };

std::string s2 { "Marc"}; // fits in SSO buffer
UserName u3 { std::move(s2) };

Remember that each move is the same as copy now.

For const std::string&:

  • u1 - two copies: one copy from the input string literal into a temporary string argument, then another copy into the member variable.
  • u2 - one copy: the existing string is bound to the reference argument, and then we have one copy in the member variable.
  • u3 - one copy: rvalue reference is bound to the input parameter at no cost, later we have a copy in the member field.

For std::string_view:

  • u1 - one copy: no copy for the input parameter, there's only one copy when mName is initialized.
  • u2 - one copy: no copy for the input parameter, as string_view creation is fast, and then one copy into the member variable.

For std::string:

  • u1 - two copies: the input argument is created from a string literal, and then there's a copy inmName.
  • u2 - two copies: one copy into the argument and then the second copy into the member.
  • u3 - two copies: one copy into the argument (move means copy) and then the second copy into the member.

As you see for short strings passing by value might be "slower" when you pass some existing string, because you have two copies rather than one. On the other hand, the compiler might optimize the code better when it sees a value. What's more, short strings are cheap to copy so the potential "slowdown" might not be even visible.

A Note on Universal (Forwarding) References

There's also another alternative:

class UserName {
    std::string mName;

public:
    template<typename T>
    UserName(T&& str) : mName(std::<T>forward(str)) { }
};

In this case, we ask the compiler to do the hard work and figure out all the proper overloads for our initialization case. It's not only working for input string arguments, but actually other types that are convertible to the member object.

For now, I'd like to stop here and not go into details. You may experiment with that idea and figure out if this is the best option for string passing. What are the pros and cons of that approach?

Some more references:

  • Universal vs Forwarding References in C++ | Petr Zemek
  • Universal References in C++11—Scott Meyers : Standard C++

Summary

All in all, passing by value and then moving from a string argument is the preferred solution in Modern C++. You have simple code and better performance for larger strings. There's also no risk with dangling references as in the string_view case.

I've also asked a question @Twitter about preferences, here's the summary:

How do you init a string class member in a constructor? #cpp

- Bartlomiej Filipek (@fenbf) July 29, 2018

What do you think? Which one do you use in your code? Maybe there's some other option?

Data Types Strings

Published at DZone with permission of Bartłomiej Filipek, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • DataWeave: Play With Dates (Part 1)
  • Tired of Messy Code? Master the Art of Writing Clean Codebases
  • The Long Road to Java Virtual Threads
  • Exploring Exciting New Features in Java 17 With Examples

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: