【发布时间】:2014-11-26 15:43:32
【问题描述】:
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 值。
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);
}
这仍然有缺点,它不能是虚拟的,并且它必须在标题中(尽管希望模块最终会呈现这种情况),但这似乎相当可教。
【问题讨论】:
-
好吧,房间里有人说它的约束不正确并且不接受字符串文字——也许我们可以抄送@HowardHinnant?在我看来也不对。 Video of the talk
-
@PiotrS.: MSVC 没有别名,所以我忘记了:/
-
@MooingDuck 嘿,VS2013 支持别名,它们的 stdlib 实现了 C++14 type_trait 别名。
-
@bames53:是吗?我想知道我怎么忽略了这一点!
标签: c++ c++11 perfect-forwarding enable-if forwarding-reference