C++ 的访问者模式 std::visit 中的完美转发
我每周都在看Jason Turner C ++,遇到了这个代码片段。
template<typename... B>
struct Visitor: B...{
template<typename... T>
Visitor(T&&...t): B(std::forward<T>(t))...{}
using B::operator()...;
};
template<typename...T>
Visitor(T...) -> Visitor<std::decay_t<T>...>;
int main(){
std::array<std::variant<double, int, std::string>, 6> a{3.2, 2, 4, 6, 2.4, "foo"};
int intTotal = 0;
double doubleTotal = 0.0;
Visitor visitor {[&intTotal](const int i){intTotal += i;},
[&doubleTotal](const double d) { doubleTotal += d;},
[](const std::string& c) { }};
std::for_each(begin(a), end(a), [&visitor](const auto &v) {std::visit(visitor, v);});
std::cout << intTotal << " " << doubleTotal << std::endl;
}
我可以摆脱完美的转发以及扣除指南。这不是C ++中的最佳实践。在什么情况下,效率降低了?
template<typename... B>
struct Visitor: B...{
Visitor(const B&... b): B(b)... { }
using B::operator()...;
};
e
I was watching Jason Turner C++ weekly and I came across this code snippet.
template<typename... B>
struct Visitor: B...{
template<typename... T>
Visitor(T&&...t): B(std::forward<T>(t))...{}
using B::operator()...;
};
template<typename...T>
Visitor(T...) -> Visitor<std::decay_t<T>...>;
int main(){
std::array<std::variant<double, int, std::string>, 6> a{3.2, 2, 4, 6, 2.4, "foo"};
int intTotal = 0;
double doubleTotal = 0.0;
Visitor visitor {[&intTotal](const int i){intTotal += i;},
[&doubleTotal](const double d) { doubleTotal += d;},
[](const std::string& c) { }};
std::for_each(begin(a), end(a), [&visitor](const auto &v) {std::visit(visitor, v);});
std::cout << intTotal << " " << doubleTotal << std::endl;
}
I can get rid of the the perfect forwarding as well as the deduction guide. Is this not considered the best practice in C++. In what situations is it less efficient?
template<typename... B>
struct Visitor: B...{
Visitor(const B&... b): B(b)... { }
using B::operator()...;
};
e
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
此构造函数的所有参数现在都将是 const 引用...
...并且如果这些子类的构造函数中的任何一个采用非 const 引用的相应参数,这将是错误的并且无法编译。
尝试在此类中使用您的访问者,看看它是否有效。你的模板实例的构造函数应该是这样的
All your parameters to this constructor will now be
const
references...... and if any of these subclasses' constructors take a corresponding parameter that's a non-
const
reference, this will be ill-formed and not compile.Try using your visitor with this class, to see if it works. Your template instance's constructor should turn out to be something like
通过 const 引用传递会复制 lambda 而不是移动它们。
对于你的情况来说这并不重要,但是 lambda 是否按值捕获事物就会很重要。如果 lambda 的复制成本很高,则可能会损害性能,或者如果无法复制 lambda(因为它们具有不可复制的状态,例如
[x = std::make_unique() ),则会导致编译错误](){}
)。如果您通过 const 引用传递,是的。但如果您传递转发引用,则不会。
具有一组独立模板参数的构造函数是一个问题。但是如果你使用相同的模板参数(并且继承自
std::decay_t...
),它仍然无法工作,因为内置的推导指南拒绝进行完美转发。如果传递左值,模板参数推导将不起作用。更好的问题是为什么要有构造函数。如果删除它,该类型将变成一个聚合,并且一切都将继续像以前一样工作。
它甚至可以让你删除推导指南(除了 Clang 还不支持)。
Passing by a const reference would copy the lambdas instead of moving them.
It doesn't matter in your case, but it will matter if the lambdas capture things by value. It can hurt performance if the lambdas are expensive to copy, or cause a compilation error if the lambdas can't be copied (because they have non-copyable state, e.g.
[x = std::make_unique<int>()](){}
).If you pass by const reference, yes. But if you pass by a forwarding reference, no.
The constructor having an independent set of template parameters is one problem. Buf if you used the same template parameters (and inherited from
std::decay_t<B>...
instead), it still wouldn't work, because built-in deduction guides refuse to do perfect forwarding. The template argument deduction wouldn't work if you passed an lvalue.A better question is why have a constructor at all. If you remove it, the type becomes an aggregate and everything continues to work as before.
It even lets you remove the deduction guide (except that Clang doesn't support that yet).