默认构造函数std::chrono::duration定义如下:
constexpr duration() = default;
(例如,请参阅cppreference.com或libstdc ++源代码.)
但是,cppreference.com也谈到了constexpr构造函数:
constexpr构造函数必须满足以下要求:
...
必须在构造函数初始化列表中或通过成员大括号或相等的初始化程序初始化每个基类和每个非静态成员.另外,所涉及的每个构造函数都必须是constexpr构造函数,并且每个大括号或者相等的初始化程序的每个子句都必须是一个常量表达式
如果我对默认构造函数感到困惑,cppreference.com似乎说默认的构造函数与= default隐式构造函数的定义不同.
然而,rep(大多数)持续时间的类型是裸整数类型.所以,不应该将显式= default默认构造函数duration等效于
constexpr duration() {}
当然会保留duration::rep 未初始化类型的整数成员变量?而且,事实上,是不是标准的行为duration,使得缺省构造的值是未初始化?(但我找不到明确说明这一点的参考文献.)
在那么如何才能= default为构造duration是constexpr,如果离开一个非静态成员变量初始化?我错过了什么?
考虑以下结构,std::optional其中包含一个绝对具有“正常”默认构造函数的类型。
#include <optional>
#include <string>
struct Foo
{
Foo() = default;
const std::optional<std::string> m_value;
};
bool function()
{
Foo foo;
return bool(foo.m_value);
}
Run Code Online (Sandbox Code Playgroud)
使用 clang 9 编译以下内容(使用系统的默认值libstdc++,对于它的 gcc 8)会给出一个意外警告:
<source>:6:5: warning: explicitly defaulted default constructor is implicitly deleted [-Wdefaulted-function-deleted]
Foo() = default;
^
<source>:7:38: note: default constructor of 'Foo' is implicitly deleted because field 'm_value' of const-qualified type 'const std::optional<std::string>' (aka 'const optional<basic_string<char> >') would not be initialized
const std::optional<std::string> m_value;
^
Run Code Online (Sandbox Code Playgroud)
Foo foo;由于它使用了所述已删除的构造函数,因此也存在硬错误。
二十多年前,我会(也没有)想到对POD结构执行二进制I / O:
struct S { std::uint32_t x; std::uint16_t y; };
S s;
read(fd, &s, sizeof(s)); // assume this succeeds and reads sizeof(s) bytes
std::cout << s.x + s.y;
Run Code Online (Sandbox Code Playgroud)
(我忽略了填充和字节顺序问题,因为它们不属于我要问的问题。)
“很明显”,我们可以读入s和编译器需要假设的内容s.x和s.y是别名通过read()。因此,s.x在read()不是undefined行为之后(因为s未初始化)。
同样的情况
S s = { 1, 2 };
read(fd, &s, sizeof(s)); // assume this succeeds and reads sizeof(s) bytes
std::cout << s.x + s.y;
Run Code Online (Sandbox Code Playgroud)
编译器无法假定s.x仍在。1之后read()。
快进到现代世界,我们实际上必须遵循别名规则并避免未定义的行为,依此类推,而我无法向自己证明这是允许的。
例如,在C …
我已经使用std::chrono了多年,并观看了许多 Howard Hinnant 关于图书馆设计和使用的演讲。我喜欢它,我想我一般都理解它。然而,最近,我突然意识到我不知道如何实际和安全地使用它来避免未定义的行为。
在我通过几个案例为我的问题做准备时,请耐心等待。
让我们从我认为“最简单”的std::chrono::duration类型开始,
nanoseconds. 它的最小rep大小是 64 位,这意味着在实践中它会是std::int64_t,因此可能没有标准不需要的“剩余”可选表示位。
这个函数显然并不总是安全的:
nanoseconds f1(nanoseconds value)
{ return ++value; }
Run Code Online (Sandbox Code Playgroud)
如果value是nanoseconds::max(),那么它就会溢出,我们可以用 clang 7 的 UBSan( -fsanitize=undefined)来确认:
runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be
represented in type 'std::__1::chrono::duration<long long,
std::__1::ratio<1, 1000000000> >::rep' (aka 'long long')
Run Code Online (Sandbox Code Playgroud)
但这没什么特别的。它与典型的整数情况没有什么不同:
std::int64_t f2(std::int64_t value)
{ return ++value; }
Run Code Online (Sandbox Code Playgroud)
当我们不能确定这value不是它的最大值时,我们首先检查,然后处理我们认为合适的错误。例如:
nanoseconds f3(nanoseconds value)
{
if(value == …Run Code Online (Sandbox Code Playgroud)