完美转发设置器上正确的`enable_if`约束是什么? [英] What's the correct `enable_if` constraint on perfect forwarding setter?

查看:101
本文介绍了完美转发设置器上正确的`enable_if`约束是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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);
    }
};

该示例使用带有转发参考的模板函数,模板参数Stringenable_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`约束是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆