【发布时间】:2016-04-11 12:50:41
【问题描述】:
考虑以下示例:
第一个编译单元:
#include <vector>
#include <string>
#include <initializer_list>
#include <iostream>
struct DoubleString
{
std::string one;
std::string two;
};
class E
{
public:
E(std::initializer_list<DoubleString> init) : stringVec(std::move(init))
{}
void operator()()
{
for (auto const & x : stringVec)
{
std::cout << x.one << " " << x.two << std::endl;
}
}
private:
std::initializer_list<DoubleString> stringVec;
};
class F
{
public:
F( const std::string & one, const std::string & two) : e{ {one, two} }
{ }
void operator()()
{
e();
}
private:
E e;
};
class Caller
{
public:
void operator[](F f);
};
int main()
{
Caller()[ F{"This is string 1", "This is string 2"} ];
}
单独的编译单元:
void Caller::operator[](F f)
{
f();
}
另见http://coliru.stacked-crooked.com/a/b01d349fa8f22f62
使用 gcc 和 clang 编译和运行它,在一个编译单元中使用 sn-ps,输出是 "这是字符串 1 这是字符串 2"
当我将 void Caller::operator [](F f) 移动到单独的编译单元时,它仍然适用于 gcc,但会中断 clang(它打印垃圾)。 Clang 地址清理程序检测到:
==16368==错误:AddressSanitizer:堆栈缓冲区下溢地址 0x7ffc6602f388 在 pc 0x0000006d036a bp 0x7ffc6602f340 sp 0x7ffc6602f338
当我使用 std::vector 作为变量 E::stringVec 的类型时,它再次适用于 clang。
看来我误用了 std::initializer_list。是否允许将其用作变量?为什么它适用于 gcc 而不适用于 clang?
顺便说一句:我喜欢 Coliru 作为在线编译器。有谁知道,如何定义单独的编译单元?
【问题讨论】:
-
能否请您显示非工作代码,即当代码被拆分为不同的头文件和源文件时?
-
来自en.cppreference.com/w/cpp/utility/initializer_list。在原始初始化列表对象的生命周期结束后,不能保证底层数组存在。 std::initializer_list 的存储是未指定的(即它可以是自动、临时或静态只读内存,具体取决于具体情况)。
-
这就像你正在处理一个指向堆栈中某处的悬空指针。
-
@JoachimPileborg:我编辑了这篇文章。希望有帮助
-
同样来自 kukyakya 上面提供的链接 “初始化器列表可以实现为一对指针或指针和长度。复制
std::initializer_list不会复制底层对象。”.
标签: c++ c++11 g++ initializer-list clang++