在C++11标准中引入了common_type,在C++14中引入了common_type_t,其实只要看名字,大概也能猜出这个这个元编程库类的作用。它主要是用来获取参数的共有类型的,需要注意的,它获取的类型是退化的,因为它内部调用了std::decay。先看一个原来的例程:
template <class... Args> auto Rorder(Args &&...args) {
std::vector<std::common_type_t<Args...>> vec{};
bool bOk = false;
(bOk = ... = (vec.push_back(args), false));
return vec;
}
int main() {
std::vector<int> vec = Rorder(1, 2, 3, 4, 5, 6, 'a');
for (auto &v : vec) {
std::cout << v << std::endl;
}
}
这个例程的作用是将输入的排序逆序。它里面的 common_type_t其实就是保证vec存储的数据的一致性。在标准库的定义中,如果std::common_type_t中的参数列表都可以转化成一个共同类型,则类型存在,否则不存在。
它特别适合于在算术运算的场景下应用(比如重载运算符),能确保数据类型的一致性。
common_type的定义如下:
template< class... T >
struct common_type;
它的使用比较有意思:
1、如果无模板参数,则无类型。即sizeof…(T) 为零,则无成员 type
2、如果一个模板参数,则等同于这个类型(std::common_type
3、如果有两个模板参数,要找一个可退化到相同的类型,否则无type;如果有自定义物化,则使用该特化;另外对于一些the conditional operator的操作按照相关的标准定义来操作。
同时C++20中的新定义参看相关的新标准
4、如果大于两个模板参数,则等同于两个模板参数,否则无type
注意,其参数类型是完整类型,但使用后为退化的类型,即可以从cv限定到无cv限定。
看一个库里的例子:
#include
#include
#include
// std::chrono already finds the greatest common divisor,
// likely using std::common_type<>. We make the type
// deduction externally.
template <typename T, typename S>
constexpr auto durationDiff(const T &t, const S &s) -> typename std::common_type<T, S>::type {
typedef typename std::common_type<T, S>::type Common;
return Common(t) - Common(s);
}
int main() {
using namespace std::literals;
constexpr auto ms = 30ms;
constexpr auto us = 1100us;
constexpr auto diff = durationDiff(ms, us);
std::cout << ms.count() << " - " << us.count() << " = " << diff.count() << '\n';
return 0;
}
这个例子很明白,运行后退化到了微秒。这个就好了,计算时间时可以省转换的代码过程。然后再看一个返回值的例子:
template <typename T1, typename T2> std::common_type_t<T1, T2> CurMin(T1 t1, T2 t2) {
return t2 > t1 ? t1 : t2;
}
int main() {
auto r1 = CurMin(11.7, 3.5);
auto r2 = CurMin(7.1, 9);
std::cout << "CurMin value:" << r1 << std::endl;
std::cout << "CurMin value:" << r2 << std::endl;
return 0;
}
那么在处理常见的继承上呢?看下面的程序:
struct B {
B() { std::cout << "cur is B" << std::endl; }
};
struct D : B {
D() { std::cout << "cur is B" << std::endl; }
};
void testClassD() {
typedef std::common_type<D, B>::type T1;
typedef std::common_type<D *, B *>::type T2;
T1 t1;
std::cout << std::is_same_v<T2, B *> << std::endl;
std::cout << std::is_same_v<T2, D *> << std::endl;
}
运行结果发现都是基类型才是共同的类型,这也符合前面的说明的中的情况。
同样,在上面的两个参数中说明可以自己实现特化,那么就照葫芦画瓢,来一个例子:
struct Ex0 {
int a = 0;
};
struct Ex1 {
int a = 1;
};
namespace std {
template <> struct common_type<Ex0, Ex1> { using type = Ex0; };
}
void testOwner() {
using type = std::common_type_t<Ex0, Ex1>;
type m;
std::cout << "cur class value:" << m.a << std::endl;
}
通过运行的值可以看到确实是指定的Ex0,这就说明自定义的特化被实现了。
这些元编程中用的相关库类型,在某些场景下是非常有用并且好用的。不过还是那句话,还是要多用,多反复的印证。用得多了,自然有些不好理解的东西就会豁然开朗,不言而喻。