我可能对std :: overloaded提案和/或概念有些困惑,但是根据我对这两者的当前理解,我有以下问题:
为什么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);
答案 0 :(得分:4)
我认为这将是一个非常糟糕的主意。
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
的好处。也许就足够了。