使用C ++ Lambda表达式初始化类成员

问题描述

我想使用lambda表达式初始化一个类成员std::string filePath)。 程序可以正常编译,但是没有输出。怎么了?

#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());
}

解决方法

我在Linux 5.8.6上使用clang v10.0.1编译了您的代码。您的代码进入了核心转储。以下是valgrind的一些日志(为了避免混淆而进行了简化):

==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)

显然,函数append出问题了。实际上,您的问题是您在初始化之前使用oneStr

根据cppreference:

https://en.cppreference.com/w/cpp/language/constructor

列表中成员初始化程序的顺序无关紧要:实际的初始化顺序如下:

...

3)然后,按类定义中的声明顺序初始化非静态数据成员。

因此,filePathoneStr被初始化之前被初始化。计算lambda,捕获thisthis->oneStr未初始化。


更改声明顺序将解决此未定义行为

#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());
}

但是仍然可能无法获得预期的结果(也许 one two?)。您只会看到 one打印。这是因为oneStr首先用""初始化,然后在" two"中附加了MyString::MyString();但最终在使用" one"函数进行打印之前已分配了main

, 编译后

已更新

#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
}

在“ 1”行str对象已创建。

filePath字符串首先被初始化。 Lambda捕获this,但是oneStr尚未初始化,因为它被定义为第二个成员。

Lambda正在使用oneStr,因此没人知道结果是什么。实际上,程序会以静默方式崩溃。


如果您希望示例打印“一二”,请构造一个构造函数并在那里初始化oneStr

这是工作示例:

#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

,

我不确定您使用的是哪个编译器,但是g ++中的代码会给出分段错误,因为当调用lambda时oneStr不存在。如果您颠倒了顺序,它应该可以工作,但是请注意,str.oneStr = std :: string(“ one”)行将用“ 1”替换“ 2”。也许您想要str.oneStr = str.oneStr + std :: string(“ one”)