c++ - 完美转发 setter 的正确 `enable_if` 约束是什么?

标签 c++ c++11 perfect-forwarding enable-if forwarding-reference

Herb Sutter 的 回归基础! CppCon 上的现代 C++ 基础介绍 讨论了传递参数的不同选项,并比较了它们的性能与编写/教学的难易程度。 “高级”选项(在所有测试的情况下提供最佳性能,但对于大多数开发人员来说太难编写)是完美的转发,给出的示例 (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可以使用 任何东西设置成员,但 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 *鉴于这是演示文稿中测试的案例之一,显然它旨在与这种类型一起使用。

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

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

允许任何可以分配给成员的东西与 setter 一起使用。

我有正确的约束吗?还有其他可以改进的地方吗?对原始约束是否有任何解释,也许是断章取义?


我还想知道这个声明中复杂的、“不可教”的部分是否真的那么有益。由于我们没有使用重载,我们可以简单地依赖正常的模板实例化:

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

当然,关于 noexcept真的很重要,有人说除了移动/交换原语外不要太担心:

template <class String>
void set_name(String &&name) {
    name_ = std::forward<String>(name);
}

也许对于概念来说,约束模板不会非常困难,只是为了改进错误消息。

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

这仍然有缺点,它不能是虚拟的,而且它必须在标题中(尽管希望模块最终会呈现这种情况),但这似乎相当可教。

最佳答案

我认为你所拥有的可能是正确的,但为了不写一个简单的“我同意”的“答案”,我会提出这个建议,而不是根据正确的类型检查分配 - 是否是 lval , rval, const, 随便什么:

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

关于c++ - 完美转发 setter 的正确 `enable_if` 约束是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26147491/

相关文章:

c++ - 在没有任何服务器的情况下使用sql

c++ - 为什么更喜欢在 C++ 中签名而不是无符号?

c++ - 使用迭代器作为参数重载 << 运算符

c++ - 这些成员是否有未指定的顺序?

c++ - 这段代码合法吗? (C++0x 移动语义)

c++ - 评价顺序

c++ - 到 std::function<R(ARGS...)> 的可变参数模板转换适用于 GCC 而不是 MSVC2013,为什么?

c++ - 为什么 fetch_sub 不是释放操作?

c++ - 完善的转发功能推导出冲突错误

c++ - 在完美转发中 `decltype(std::forward<Args>(args))...` 和 Args&& 有什么区别