问题描述
Herb Sutter的回到基础知识! CppCon上的Modern C ++ 的要点讨论了传递参数的不同选项,并比较了它们的性能与编写/教学的难易程度. 高级"选项(在所有测试的情况下都提供了最佳性能,但是对于大多数开发人员而言太难了)是完美的转发,示例给出了:
Herb Sutter's Back to the Basics! Essentials of Modern C++ presentation at CppCon discussed different options for passing parameters and compared their performance vs. ease of writing/teaching. The 'advanced' option (providing the best performance in all the cases tested, but too difficult for most developers to write) was perfect forwarding, with the example given (PDF, pg. 28):
class employee {
std::string name_;
public:
template <class String,
class = std::enable_if_t<!std::is_same<std::decay_t<String>,
std::string>::value>>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<std::string &, String>::value) {
name_ = std::forward<String>(name);
}
};
该示例使用带有转发参考的模板函数,模板参数String
受enable_if
约束.但是,约束似乎是不正确的:似乎是在String
类型不是std::string
的情况下才可以使用此方法,这没有任何意义.这意味着该std::string
成员可以使用(除了值之外的任何东西)进行设置.
The example uses a template function with forwarding reference, with the template parameter String
constrained using enable_if
. However the constraint appears to be incorrect: It seems to be saying this method may be used only if the String
type is not a std::string
, which makes no sense. That would mean that this std::string
member can be set using anything but an std::string
value.
using namespace std::string_literals;
employee e;
e.set_name("Bob"s); // error
我考虑过的一种解释是,这里有一个简单的错字,并且约束原本是std::is_same<std::decay_t<String>, std::string>::value
而不是!std::is_same<std::decay_t<String>, std::string>::value
.但是,这意味着该setter不能使用,例如const char *
,并且显然是打算使用此类型的,因为这是演示文稿中测试的情况之一.
One explanation I considered was that there's a simple typo and the constraint was intended to be std::is_same<std::decay_t<String>, std::string>::value
instead of !std::is_same<std::decay_t<String>, std::string>::value
. However that would imply that the setter doesn't work with, e.g., const char *
and it obviously was intended to work with this type given that that's one of the cases tested in the presentation.
在我看来,正确的约束条件更像是:
It seems to me that the correct constraint is more like:
template <class String,
class = std::enable_if_t<std::is_assignable<decltype((name_)),
String>::value>>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<decltype((name_)), String>::value) {
name_ = std::forward<String>(name);
}
允许将任何可以分配给成员的东西与设置员一起使用.
allowing anything that can be assigned to the member to be used with the setter.
我有正确的约束条件吗?还有其他可以改进的地方吗?对于原始约束是否有任何解释,也许是出于上下文?
Have I got the right constraint? Are there other improvements that can be made? Is there any explanation for the original constraint, perhaps it was taken out of context?
我也想知道这个声明中复杂的,无法学习的"部分真的有那么大的好处吗?由于我们不使用重载,因此我们可以简单地依靠常规模板实例化:
Also I wonder if the complicated, 'unteachable' parts of this declaration are really that beneficial. Since we're not using overloading we can simply rely on normal template instantiation:
template <class String>
void set_name(String &&name) noexcept(
std::is_nothrow_assignable<decltype((name_)), String>::value) {
name_ = std::forward<String>(name);
}
当然还有一些关于noexcept
是否真正重要的争论,有些人说除了移动/交换原语外不要担心太多:
And of course there's some debate over whether noexcept
really matters, some saying not to worry too much about it except for move/swap primitives:
template <class String>
void set_name(String &&name) {
name_ = std::forward<String>(name);
}
也许有了概念,仅仅为了改进错误消息,对模板进行约束并不是没有道理的困难.
Maybe with concepts it wouldn't be unreasonably difficult to constrain the template, simply for improved error messages.
template <class String>
requires std::is_assignable<decltype((name_)), String>::value
void set_name(String &&name) {
name_ = std::forward<String>(name);
}
这仍然具有缺点,即它不能是虚拟的,并且必须位于标头中(尽管希望模块最终能够解决该问题),但这似乎是可以讲授的.
This would still have the drawbacks that it can't be virtual and that it must be in a header (though hopefully modules will eventually render that moot), but this seems fairly teachable.
推荐答案
我认为您所拥有的可能是正确的,但是为了不写一个简单的我同意"的答案",我会建议您这样做它将根据正确的类型检查分配-是否是lval,rval,const等:
I think what you have is probably right, but in the interest of not writing an "answer" that is simply "I agree", I will propose this instead that will check assignment based on the correct types - whether it's an lval, rval, const, whatever:
template <class String>
auto set_name(String&& name)
-> decltype(name_ = std::forward<String>(name), void()) {
name_ = std::forward<String>(name);
}
这篇关于完美转发设置器上正确的`enable_if`约束是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!