前景提要
不好意思问题题目有点绕,请看以下类模板代码 #pragma once #include <functional> #include <type_traits> template <typename... Types> class Tuple; template <typename Head, typename... Tail> class Tuple<Head, Tail...> { private: Head head; Tuple<Tail...> tail; public: Tuple() {}; Tuple(Head const& h, Tuple<Tail...> const& t) : head(h), tail(t) {} Tuple(Head const& h, Tail const& ... t) : head(h), tail(t...) {} template <typename VHead, typename... VTail> \\ 构造函数 1 Tuple(VHead&& vhead, VTail&&... vtail) : head(std::forward<VHead>(vhead)), tail(std::forward<VTail>(vtail)...) {} template <typename VHead, typename... VTail> \\ 构造函数 2 Tuple(Tuple<VHead, VTail...> const & other) : head(other.getHead()), tail(other.getTail()) {} Head& getHead() { return head; } Head const & getHead() const { return head; } Tuple<Tail...> & getTail() { return tail; } Tuple<Tail...> const & getTail() const { return tail; } }; template <> class Tuple<> {};
当我们试图以一个 Tuple 类型来构造另外一个 Tuple 的时候,有两种情况,一种是构造相同模板参数 tuple,另一种是构造不同但可以相互转换的模板参数,代码如下: Tuple<int, double, string> t(17, 3.14, "Hello world"); Tuple<long int, long double, string> b = t; \\ 情形 1 Tuple<long int, long double, string> b = t; \\ 情形 2
在这个类模板定义下,上述代码是编译不过去的,原因是构造函数 1 的优先级比构造函数 2 高,所以编译器没有调用复制构造函数 2 。因此我们需要用 std::enable_if_t 去 disable 构造函数,所以构造函数 1 和 2 可以被改写为下面两种:
第一种: std::enable_if_t 为默认模板参数 template <typename VHead, typename... VTail, typename = std::enable_if_t<sizeof...(VTail)==sizeof...(Tail) > > Tuple(VHead&& vhead, VTail&&... vtail) : head(std::forward<VHead>(vhead)), tail(std::forward<VTail>(vtail)...) {} template <typename VHead, typename... VTail, typename = std::enable_if_t<sizeof...(VTail)==sizeof...(Tail) > > Tuple(Tuple<VHead, VTail...> const & other) : head(other.getHead()), tail(other.getTail()) {}
第二种: std::enable_if_t 为默认匿名 Nontype 模板参数 template <typename VHead, typename... VTail, std::enable_if_t<sizeof...(VTail)==sizeof...(Tail) > =0 > Tuple(VHead&& vhead, VTail&&... vtail) : head(std::forward<VHead>(vhead)), tail(std::forward<VTail>(vtail)...) {} template <typename VHead, typename... VTail, std::enable_if_t<sizeof...(VTail)==sizeof...(Tail) > =0 > Tuple(Tuple<VHead, VTail...> const & other) : head(other.getHead()), tail(other.getTail()) {}
那么问题来了: 为什么加了相同的 enable_if 条件,就使得构造函数 2 的优先级比构造函数 1 的优先级高了呢(构造函数 1 、2 的条件都是 VTails 类型的个数和 Tail 的类型个数相同。 enable_if 的两种写法中第二种是 CPP reference 推荐写法, 原话是这么说的:A common mistake is to declare two function templates that differ only in their default template arguments. This does not work because the declarations are treated as redeclarations of the same function template (default template arguments are not accounted for in function template equivalence). 也就是说如果用第一种写法很可能会把构造函数 1 和构造函数 2 当成同一个函数声明和定义两次,当然本例中不存在这个问题,因为构造函数 1 和构造函数 2 在大部分情形下含有的参数个数不同,但至少这两种 enable_if 的写法是等价的。对于情形 1 构造相同模板参数 tuple 两种 enable_if 写法 gcc 和 msvc 均可以编译过去,但是对于情形 2 构造不同但可以相互转换的模板参数,只有第一种写法也就是默认模板参数方法,msvc 和 gcc 可以编译过去,这是为什么呢?
感谢您看完如此长的问题描述,谢谢!
编译器分别为 msvc 16.7 和 gcc 10