CTTCG 06 Move Semantics and enable_if

Perfect forwarding:

template<typename T>
void f (T&& val) {
    g(std::forward<T>(val)); // perfect forward val to g()
}

注意,完美转发只对模板参数有效,对依赖于模板参数的类型也是无效的,比如:typename T::iterator&& 是无效的,仍表示严格的右值引用。

完美转发也不能加 const 属性。添加 const 属性后表示常右值引用(而且这种形式很少被使用)。

尝试用完美转发简化代码

设想一个 Person 类,里面只有一个 std::string 类型的 name 数据。已经实现了复制构造函数和移动构造函数。从数据的构造函数有 std::string &&std::string const & 两个重载版本。使用完美转发可以同时写出匹配这两种参数的函数模板,减少代码量。

💡 事实上,代码量并不会减少,反而会变得复杂。在这种必定需要复制的场合,最好的方式还是接受 std::string 的值类型。下面的讨论假定必须使用模板实现。

问题在哪里?

完美转发匹配的优先级太高了,在传入一个 Person & 对象的时候,Person const & 复制构造函数匹配优先度低,从而调用的是函数模板。而给定的函数模板是为字符串类型写的,显然 Person 不能用于初始化 std::string,因而无法编译。

可以用 std::enable_if 来要求参数必须是字符串。在 C++20 中,可以用 concept 来加限制。

💡 std::enable_if_t 这样形式的 _t 后缀是 C++14 引入的。而 std::is_convertable_v 这样的 _v 后缀是 C++17 引入的。

函数模板还有什么问题?

若为类中的特殊函数提供函数模板,默认的特殊函数并不会被删除。在都能够匹配的场合中,默认的特殊函数优先级更高。因而有时候需要显式 delete。

但是最好还是不要为特殊函数提供模板。