什么是完美转发设置器的正确`enable_if`约束?

bam*_*s53 17 c++ enable-if perfect-forwarding c++11 forwarding-reference

Herb Sutter 回归基础!CppCon 上的现代C++演示要点讨论了传递参数的不同选项,并将其性能与写作/教学的简易性进行了比较."高级"选项(在所有测试的案例中提供最佳性能,但对大多数开发人员来说难以编写)是完美转发,给出了示例(PDF,第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);
    }
};
Run Code Online (Sandbox Code Playgroud)

该示例使用带转发引用的模板函数,模板参数String使用约束enable_if.然而,约束似乎是不正确的:似乎只有在String类型不是a 时才使用此方法std::string,这没有任何意义.这将意味着该std::string成员可以使用设置什么,但一个std::string值.

using namespace std::string_literals;

employee e;
e.set_name("Bob"s); // error
Run Code Online (Sandbox Code Playgroud)

我考虑的一个解释是,有一个简单的拼写错误,而且约束的目的是std::is_same<std::decay_t<String>, std::string>::value代替!std::is_same<std::decay_t<String>, std::string>::value.然而,这意味着setter不能使用,例如,const char *它显然是打算使用这种类型,因为这是在演示文稿中测试的案例之一.

在我看来,正确的约束更像是:

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);
}
Run Code Online (Sandbox Code Playgroud)

允许任何可以分配给成员的内容与setter一起使用.

我有正确的约束吗?是否可以进行其他改进?原始约束是否有任何解释,也许它是脱离背景的?


我也想知道这个宣言中复杂的,"无法触及"的部分是否真的有益.由于我们没有使用重载,我们可以简单地依赖于普通的模板实例化:

template <class String>
void set_name(String &&name) noexcept(
  std::is_nothrow_assignable<decltype((name_)), String>::value) {
    name_ = std::forward<String>(name);
}
Run Code Online (Sandbox Code Playgroud)

当然,对于是否noexcept真的很重要还有一些争论,有些人说除了移动/交换原语之外不要太担心它:

template <class String>
void set_name(String &&name) {
    name_ = std::forward<String>(name);
}
Run Code Online (Sandbox Code Playgroud)

也许对于概念,限制模板并不是不合理的困难,仅仅是为了改进错误消息.

template <class String>
  requires std::is_assignable<decltype((name_)), String>::value
void set_name(String &&name) {
    name_ = std::forward<String>(name);
}
Run Code Online (Sandbox Code Playgroud)

这仍然有缺点,它不能是虚拟的,它必须在一个标题中(虽然希望模块最终会产生这种模糊),但这似乎相当可教.

Bar*_*rry 4

我认为你所拥有的可能是正确的,但为了不写一个简单的“我同意”的“答案”,我将建议这样做,它将根据正确的类型检查分配 - 无论是 lval、rval、常量,无论什么:

template <class String>
auto set_name(String&& name) 
-> decltype(name_ = std::forward<String>(name), void()) {
    name_ = std::forward<String>(name);
}
Run Code Online (Sandbox Code Playgroud)