I want to initialize one class member (std::string filePath
) using lambda expression.
Program is compiling fine however there is no output. What is wrong here?
#include <iostream>
#include <string>
class MyString
{
public:
std::string filePath = [this] ()
{
return oneStr.append(" two");
}();
std::string oneStr;
};
int main()
{
MyString str;
str.oneStr = std::string(" one");
printf("%s", str.oneStr.c_str());
}
I compiled your code with clang v10.0.1 on Linux 5.8.6. Your code bumped into an core dump. Here are some logs of valgrind
(simplified in order not to be confusing):
==12170== Conditional jump or move depends on uninitialised value(s)
==12170== at 0x49BEAFE: _M_check_length (basic_string.h:322)
==12170== by 0x49BEAFE: std::string::append(char const*) (basic_string.h:1238)
==12170== by 0x10944A: MyString::filePath::{lambda()#1}::operator()[abi:cxx11]() const (test.cc:9)
==12170== by 0x109390: MyString::MyString() (test.cc:7)
==12170== by 0x109270: main (test.cc:17)
Obviously there's something wrong with the function append
. In fact your issue is that you're using oneStr
before its initialization.
According to cppreference:
https://en.cppreference.com/w/cpp/language/constructor
The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:
...
3) Then, non-static data member are initialized in order of declaration in the class definition.
As a result, filePath
is initialized before oneStr
gets initialized. The lambda is evaluated, with this
captured, with this->oneStr
not initialized.
Changing the order of declaration would fix this undefined behavior:
#include <iostream>
#include <string>
class MyString
{
public:
std::string oneStr;
std::string filePath = [this] ()
{
return oneStr.append(" two");
}();
};
int main()
{
MyString str;
str.oneStr = std::string(" one");
printf("%s", str.oneStr.c_str());
}
But still you may not get expected result (maybe one two
?). You will see only one
printed. That's because oneStr is firstly initialized with ""
, then appended with " two"
in MyString::MyString()
; but finally assigned with " one"
before printing in the main
function.
updated once compiled.
#include <iostream>
#include <string>
struct MyString{
std::string filePath = [this] () {
return oneStr.append(" two");
}();
std::string oneStr;
};
int main(){
MyString str; // 1
str.oneStr = std::string(" one"); // 2
std::cout << str.oneStr << '\n'; // 3
}
At the line "1" str
object is created.
filePath
string is initialized first. Lambda capture this
, but oneStr
is not yet initialized, because is defined as second member.
Lambda is using oneStr
, so nobody knows what is the result. Actually program crashes silently.
If you want the example to print " one two", do a constructor and initialize oneStr
there.
Here is working example:
#include <iostream>
#include <string>
struct MyString{
MyString(std::string oneStr) : oneStr(std::move(oneStr)){}
std::string oneStr;
std::string filePath = [this] () {
return oneStr.append(" two");
}();
};
int main(){
MyString str(" one");
std::cout << str.oneStr << '\n';
}
https://gcc.godbolt.org/z/93xbcr
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With