为什么在std::overload而不是concepts/constexpr如果在std::visit中?
Why std::overload instead of concepts/constexpr if in std::visit?
我可能对std::overload提案和/或概念有点困惑,但根据我目前对两者的理解,我有以下问题:
为什么 C++20 不只是概念化/如果 constexprify std::visit,所以它根据传递给它的参数类型知道该怎么做。
例如,为什么我们不能让 std::visit 根据传递的参数概念修改它的行为(它所需要的只是函数在前,变体在后)。
因此,例如,这两个visit
都接受 3 个参数,但逻辑不同。
std::variant<int, double> v1=4.7;
std::variant<bool, std::string> v2=false;
// calls lambda that is a better fit(double one)
std::visit([](int& a){std::cout<< sizeof (a);},[](double& a){std::cout << a;} , v1);
// calls lambda on variant v1 and then variant v2
std::visit([](auto& a){}, v1, v2);
我认为这将是一个非常糟糕的主意。
visit
现在在概念上是一个简单的功能。它这样做:
template <typename F, typename... Variants>
decltype(auto) visit(F f, Variants... vs) {
return f(std::get<vs.index()>(vs)...);
}
当然,除了vs.index()
不是一个常量表达式,所以你不能只是这样做,你需要整个复杂的实现来做到这一点。但关键是 -visit
一堆变体只是调用f
每个变体的当前替代方案。这很容易推理。
让通用算法根据它传递的类型在语义上做不同的事情是一个非常糟糕的主意。这意味着你不能真正推理代码的正确性 - 因为它基本上可以做任何事情。OP 中的最后一个示例就是一个很好的例子:
std::visit([](auto&){}, v1, v2); // #1
std::visit([](auto&, auto&){}, v1, v2); // #2
今天,#1
(OP 示例)无法编译,因为您要将一元函数传递给二进制访问者。这是一件好事。但是有了这个建议,两者都会编译并做截然不同的事情。一个将按顺序访问每个变体,另一个将一起访问每个变体。
用户意图是什么?也许#1
是一个错误,用户要么忘记了参数,要么提供了一个在大多数情况下(但不是所有)都作为二元访问者工作的访问者,但在所有情况下都作为一元访问者工作?
OP 中的另一个例子不太糟糕,它具有:
visit(f1, f2, f3, v1, v2, v3)
表示对三个组合功能的三元访问,而不是对一个功能进行五元访问:
visit(overload(f1, f2, f3), v1, v2, v3)
但是,实现起来非常复杂,收益很小(用户可以将函数包装在他们这边,对吗?),你必须开始问这样的问题:如果你有一个可调用的variant
怎么办?
如果你打算使用不同的语法,我已经看到了几个代码示例,其中人们像这样实现访问语法:
visit2(v1, v2, v3)(f1, f2, f3)
这非常容易实现:
template <typename... Variants>
auto visit2(Variants&&... variants) {
return [&](auto&&... fs) -> decltype(auto) {
return std::visit(overload(FWD(fs)...), FWD(variants)...);
};
}
并且具有将变体放在首位而不必编写overload
的好处。也许这就足够了。
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 如果我std::dynamic_pointer_cast并且底层dynamic_cast的结果为null,那么返回的sh
- 如果 std::vector::clear() 不是静态的,如何在没有实例的情况下调用它?
- C++如果必须在编译时确定大小,std::array 有什么意义?
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?
- 如果我重新定义 sqrt 函数,为什么使用 std::sqrt 失败?
- 如果模板没有可变参数,则 Lambda 被推导出为 std::function
- VSCode 说 std::chrono 是模棱两可的,如果运算符<<重载
- 如果在 2 个线程中使用,是否值得将size_t声明为 std::atomic?
- std::unordered_map 如果输入大小已知,如何优化批量插入
- 如果 lambda 没有指定的类型,std::function 如何接受 lambda?
- 如果只有 std::auto_ptr 可用,我是否仍应该使用智能指针?
- 为什么使用 std::vector 的代码不能编译,但使用 std::unique_ptr 如果没有 noexcept
- 使用用户定义的类型 UDT 实例化 std::atomic<>。如果 UDT 具有虚函数,则 l 墨水将失败。为什么?
- 如果我们不从 std::qsort 返回 0 会发生什么?
- 如果 std::function 捕获了unique_ptr,如何复制它?
- 使用 std::istream_iterator 时,它似乎跳过了空文件行 - 如果可能的话,如何避免这种情况?
- 如果迭代器没有因插入而无效,则使用std::find和C::insert()是线程安全的
- 如果真的需要std::move,我们应该什么时候声明右值refs
- 如果使用lambda,std::unique_ptr如何没有大小开销