Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reference in container?

Tags:

c++

reference

I'm having an issue with passing a string as reference to a lambda, when it is in a container. I guess it disappears (goes out of scope) when I call the init() function, but why? And then, why doesn't it disappear when I just pass it as a string reference?

#include <iostream>
#include <string>

struct Foo {
  void init(int num, const std::string& txt)
  {
    this->num = num;
    this->txt = txt;
  }

  int num;
  std::string txt;
};

int main()
{
  auto codegen1 = [](std::pair<int, const std::string&> package) -> Foo* {
    auto foo = new Foo;
    foo->init(package.first, package.second); //here string goes out of scope, exception
    return foo;
  };

  auto codegen2 = [](int num, const std::string& txt) -> Foo* {
    auto foo = new Foo;
    foo->init(num, txt);
    return foo;
  };

  auto foo = codegen1({3, "text"});
  auto foo2 = codegen2(3, "text");
  std::cout << foo->txt;
} 

I know the way to go would be to use const std::pair<int, std::string>&, but I want to understand why this approach doesn't work.

like image 877
Mat Avatar asked Oct 14 '21 20:10

Mat


People also ask

What is a container in reference?

It is a larger source that contains smaller works. For example, if you are citing the short story "An Occurrence at Owl Creek Bridge," the book that has the short story, Selected American Short Stories, is considered the "container," as it contains the story.

What is container in Harvard referencing?

When the cited work is part of a larger work, e.g. an essay in a collection of essays, an article in a journal, an episode of a TV series, the larger work can be considered a container that holds the cited work. The title of the container is vital to the identification of the source.

What is the container in C++?

A container is an object that stores a collection of objects of a specific type. For example, if we need to store a list of names, we can use a vector . C++ STL provides different types of containers based on our requirements.

How to reference a container in HTML?

Today, we will take a deeper look at how to reference a container because it is necessary for you to change page structure when working with design. To update a container, you can use <referenceContainer>. For example: Add links to the page header panel. To create wrap div or block, you can use containers.

What is an example of a container in a citation?

Examples of sources that have containers are articles or information from a website. An article would have a title of container in its citation because the title of the article itself would be the title of source, while the title of the periodical it is published in would be the title of container.

How do I update a referencecontainer?

To update a container, you can use <referenceContainer>. For example: Add links to the page header panel. To create wrap div or block, you can use containers. For example: Add a container into the footer container and show it on the frontend. <referenceContainer name="page.wrapper" htmlClass="my-new-page-wrapper-class second-class"/>

What is a container in Java?

A container is a holder object that stores a collection of other objects (its elements). They are implemented as class templates, which allows a great flexibility in the types supported as elements.


Video Answer


1 Answers

Part 1: This looks busted at a glance.

"text" is not a string, it's a literal that is being used to create a temporary std::string object, which is then used to initialize the std::pair. So you'd think it would make sense that the string, which is only needed transiently (i.e only until the std::pair is constructed), is gone by the time it is being referred to.

Part 2: But it shouldn't be busted.

However, temporaries that are created as part of an expression are supposed to be guaranteed to live until the end of the current "full-expression" (simplified: until the semicolon).

That's why the call to codegen2() works fine. A temporary std::string is created, and it stays alive until the call to codegen2() is complete.

Part 3: Yet it is busted, in this case.

So why does the string get destroyed prematurely in codegen1()'s case? The conversion from "text" to std::string does not happen as a sub-expression, but as part of a separate function being called with its own scope.

The constructor of std::pair that is being used here is:

// Initializes first with std::forward<U1>(x) and second with std::forward<U2>(y).
template< class U1 = T1, class U2 = T2 >
constexpr pair( U1&& x, U2&& y );

The constructor gets "text" as a parameter and the construction of the std::string is being done inside of std::pair's constructor, so the temporary variable created as part of that process gets cleaned up when we return from that constructor.

The funny thing is, if that constructor did not exist, std::pair's basic constructor: pair( const T1& x, const T2& y ); would handle this just fine. The implicit conversion to std::string would be done prior to invoking the constructor.

How do we fix this?

There's a few alternatives:

Force the conversion to happen at the right "level":

auto foo = codegen1({3, std::string("text")});

Effectively the same thing, but with a nicer syntax:

using namespace std::literals::string_literals;
auto foo = codegen1({3, "text"s});

Use a std::string_view, which removes the need for the conversion altogether:

auto codegen1 = [](std::pair<int, std::string_view> package) -> Foo* {
...
}

Though, in your case, since Foo will take ownership of the string, passing it by value and moving it around is clearly the way to go (I also took the liberty to clean up the code a bit):

struct Foo {
  Foo(int num, std::string txt)
    : num(num)
    , txt(std::move(txt))
  {}

  int num;
  std::string txt;
};

int main()
{
  auto codegen1 = [](std::pair<int, std::string> package) {
    return std::make_unique<Foo>(package.first, std::move(package.second));
  };

  auto foo = codegen1({3, "text"});

  std::cout << foo->txt;
}
like image 118
Frank Avatar answered Nov 15 '22 16:11

Frank