我完全理解,由于性能原因,operator*instd::optional不会对所包含值的实际存在进行任何检查。然而,在调试模式下,性能考虑并不重要,并且在调试模式下应该做出某种断言对我来说很有意义。
Visual Studio似乎没有这样的断言(尽管我不确定其他编译器)。
我的问题是:编译器不会在调试模式下做出这样的断言是否有任何根本原因,或者它只是一个缺失的功能?
我有多个函数返回一个std::optional<T>. 下面是一个虚构类型的例子MyType:
struct MyType {
// ...
}
std::optional<MyType> calculateOptional() {
// ... lengthy calculation
if (success) {
return MyType(/* etc */);
}
return std::nullopt;
}
Run Code Online (Sandbox Code Playgroud)
让我们假设这些函数的运行成本很高,我想避免多次调用它们。
调用它们时,我想立即测试可选项,如果它确实包含一个值,我想立即使用它,永远不要再使用它。例如,在 Swift 中,我可以使用标准if-let语句:
if let result = calculateOptional() {
// Use result var
}
Run Code Online (Sandbox Code Playgroud)
我想在 C++ 中复制这种测试和解包行为,同时在使用时尽可能保持代码干净。例如,显而易见的简单解决方案(至少对我而言)是:
if (auto result = calculateOptional()) {
MyType result_unwrapped = *result;
// Use result_unwrapped var
}
Run Code Online (Sandbox Code Playgroud)
但是您必须在 . 中解包if,或者*result在任何地方使用,这与 Swift 无关。
到目前为止,我唯一真正接近 Swift 的外观和感觉的解决方案是:
template<typename T> bool …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个函数,make_foo,它将“解开” a std::optional< foo >,返回包含的值。该函数假定可选的已启用,因此不会对optional.
我的实现如下,以及编译后的程序集以供参考。我有几个关于编译器输出的问题:
为什么这会导致代码分支?optional::operator*提供对包含值的未经检查的访问,所以我不希望看到任何分支。
为什么foo会调用析构函数?注意on_destroy()程序集中的调用。我们如何在不调用析构函数的情况下将包含的值移出可选项?
#include <optional>
extern void on_destroy();
class foo {
public:
~foo() { on_destroy(); }
};
extern std::optional< foo > foo_factory();
// Pre-condition: Call to foo_factory() will not return nullopt
foo make_foo() {
return *foo_factory();
}
Run Code Online (Sandbox Code Playgroud)
make_foo(): # @make_foo()
push rbx
sub rsp, 16
mov rbx, rdi
lea rdi, [rsp + 8]
call foo_factory()
cmp byte …Run Code Online (Sandbox Code Playgroud) 我正在尝试迭代通过 a 访问的 astd::vector<X>中包含的a 。与我的预期相反,如果我首先将其存储到副本中与直接迭代它,则行为会有所不同:struct Tstd::optional<T>std::optional<T>
#include <optional>
#include <string>
#include <vector>
// The rest is the same in both implementations
struct Object
{
std::string id;
std::vector<SomeStructType> items;
};
Object readDataFile() { /*....*/ return {}; }
bool dataFileExists() { /*....*/ return true; }
std::optional<Object> getData()
{
if (!dataFileExists()) {
return std::nullopt;
}
Object obj = readDataFile();
return obj;
}
int main()
{
// Implementation 1 - works
auto items = getData().value().items;
for (auto const& item …Run Code Online (Sandbox Code Playgroud) 我知道这艘船由于需要 ABI 损坏而已经航行,但我想知道为什么最初的实现没有决定使用一些神奇的位模式std::string来std::vector表示空的可选值。这显然需要类型的位表示中的一些“自由”值,因此例如它不适用于int,size_t但许多复杂类型具有某些永远不会出现在有效实例中的表示。
C++ 甚至可以指定某种方式来选择您的类型,例如通过专门化
std::nullopt_sentinel
Run Code Online (Sandbox Code Playgroud)
对于用户定义的类型。
我最好的猜测是,指定/实现这个的复杂性(因为您需要为许多类型实现哨兵std::)比总是使用 bool 要大得多,但我想知道是否还有其他原因。
Rust 的类似答案似乎有时会实现这一点
我不清楚通过引用将int的可选向量传递给函数的正确代码是否为:
void test_func(std::optional<std::vector<int>&> vec)
Run Code Online (Sandbox Code Playgroud)
要么
void test_func(std::optional<std::vector<int>>& vec)
Run Code Online (Sandbox Code Playgroud)
任何帮助,不胜感激。
该标准要求移动赋值运算符optional...
constexpr optional& operator=( optional&& other )
Run Code Online (Sandbox Code Playgroud)
[...] 不得参与重载决策,除非
is_move_constructible_v<T>为真并且is_move_assignable_v<T>为真。
可选值的分配lhs = rhs;可以执行以下任一操作:
lhs(如果bool(lhs) && !bool(rhs))lhs( rhsif !bool(lhs) && bool(rhs)) 或rhs给lhs(如果bool(lhs) && bool(rhs))。因此,可以选择为 的移动分配设置两组先决条件optional:
is_move_constructible_v<T> && is_move_assignable_v<T>is_move_constructible_v<T> && is_copy_assignable_v<T>其中第二种形式可以使用复制赋值 if ,bool(lhs) && bool(rhs)但移动构造 if !bool(lhs) && bool(rhs)。
对于以下两类类型,我认为当前的先决条件集存在一个不可否认的相当人为的问题:
不可移动赋值但可复制赋值、可移动构造和可复制构造的类型不能从赋值时的移动构造中受益,即使构造是赋值操作的一部分。将选择复制赋值运算optional符并复制构造或复制赋值。
既不可复制构造也不可移动分配但可移动构造且可复制分配的类型根本无法分配。
在标准化过程中是否已经考虑过这一点,optional或者是否有任何理由说明为什么没有考虑或已放弃这一点?
(免责声明:我知道如果is_move_assignable为真,则通常is_copy_assignable …
我想知道这是否被认为是std :: optional的有效用法。我有一个返回process_id(std::uint32_t值)的std::uint32_t函数,如果没有找到目标进程ID,返回一个标准的“ ”函数会更有效吗?或者返回一个std :: optional更合适吗?
例:
std::optional<std::uint32_t> FindProcessID(std::string_view process)
{
bool find = false;
if (!find)
// we fail to find the process_id and return nothing.
return std::nullopt;
else if (find)
return 100; // return the id
}
Run Code Online (Sandbox Code Playgroud)
在返回unique_ptr以及返回nullptr的同时,我也在这样做,但是我不确定是否将其视为所述功能的“滥用”,并且最好返回0并检查该值是否更好。先感谢您。
Cppreference对 的混合(可选和一些其他非可选类型)比较运算符的描述std::optional如下:
将 opt 与一个值进行比较。仅当 opt 包含值时才比较值(使用 T 的相应运算符)。否则,opt 被认为小于 value。如果 *opt 和 value 之间对应的双向比较表达式格式不正确,或者其结果不能转换为 bool,则行为未定义。
这里让我感到困惑的是:
这些格式不正确的比较的例子是什么?
为什么编译器/STL 不拒绝无效的比较而不是给我们 UB?
我熟悉,appendinstd::string返回std::string&,因此它没有资格被移出,所以结果不会被移动。
#include <string>
int main()
{
std::string s = std::string("A").append("B");
return s.size();
}
Run Code Online (Sandbox Code Playgroud)
#include <string>
int main()
{
std::string s = std::move(std::string("A").append("B"));
return s.size();
}
Run Code Online (Sandbox Code Playgroud)
在那里你可以看到,后一个例子将产生一个更少的分配,因此在这种情况下,最好移动一些看起来像临时的东西。我的问题是,为什么他们(委员会)不添加简单的过载&&,使结果append要么std::string&,或者std::string&&根据上下文?我的意思是类似于std::optional正在做的事情value。有没有一个例子可以证明这种优化是假的?
c++ ×10
stdoptional ×10
c++17 ×5
algorithm ×1
c++20 ×1
destructor ×1
return ×1
std ×1
stdstring ×1
stl ×1
unique-ptr ×1