17 模板的未来

17.1 更宽松的typename关键字规则

标准的发展方向是放宽typename关键字的限制,例如在之前不允许使用typename的地方可以使用typename关键字(C++11已经实现),以及在编译器可以推导出受限依赖型名称是类型时省略typename关键字(书中列出了几种正在被讨论的情形,但感觉只是语法糖而已,还是规范一些好)。

17.2 更多类型的非类型模板形参

目前非类型模板参数只能是整型,标准未来可能支持字符串类型和浮点型:

  • 对于C风格字符串类型的非类型模板参数,由于会将字符串转换为地址,而两个相同的字符串也可能有不同的地址,这就很难区分实例化后的两个模板是否是同一个实例
  • 对于浮点型,也是很难确定两个实例是否相等,因为浮点数存储存在误差

17.3 函数模板偏特化

实例化类模板时将首先匹配主模板,在找到了主模版后,如果存在部分匹配的偏特化类模板,将实例化偏特化类模板。原文:

When a class template is looked up, only primary templates are considered at first. If, after the selection of a primary template, it turns out that there is a partial specialization of that template with a template argument pattern that matches that of the instantiation, its definition (in other words, its body) is instantiated instead of the definition of the primary template. (Full template specializations work exactly the same way.)

对于函数模板,主模板之间可能重载,所以偏特化的模板该对应哪个主模板呢?

template<typename T>
void add (T& x, int i);     // a primary template

template<typename T1, typename T2>
void add (T1 a, T2 b);      // another (overloaded) primary template

template<typename T>
void add<T*> (T*&, int);    // Which primary template does this specialize?

17.4 模板实参指定初始化

在当前的标准下,即使模板参数有默认值,除非其后面的模板参数均使用默认值,否则必须显示指定模板实参,未来可能纳入标准的一个规则是类似C结构体的指定初始化:

template<typename T,
    typename Move = defaultMove<T>,
    typename Copy = defaultCopy<T>,
    typename Swap = defaultSwap<T>,
    typename Init = defaultInit<T>,
    typename Kill = defaultKill<T>>
class Mutator {
    // ...
};

void test(MatrixList ml)
{
    mySort (ml, Mutator <Matrix, .Swap = matrix<Swap>);
}

17.5 重载类模板

类似函数重载一样,未来可能支持类模板的重载:

template<typename T>
class Array {
    // dynamically sized array
    // ...
};
template<typename T, unsigned Size>
class Array {
    // fixed size array
    // ...
};

但是在某种程度上有点像特化:

template<typename T1, typename T2>
class Pair {
    // pair of fields
    // ...
};
template<int I1, int I2>
class Pair {
    // pair of constant integer values
    // ...
};

17.6 任意位置的参数包展开

目前标准只支持尾部的参数包展开:

template<typename... Types>
struct Front;

template<typename FrontT, typename... Types>
struct Front<FrontT, Types...> {
    using Type = FrontT;
};

不支持首部和中间位置的参数包展开:

template<typename... Types>
struct Back;

template<typename BackT, typename... Types>
struct Back<Types..., BackT> {  // ERROR: pack expansion not at the end of
    using Type = BackT;         // template argument list
};

template<typename... Types> class Tuple {
};

template<typename T, typename... Types>
struct Split;

template<typename T, typename... Before, typename... After>
struct Split<T, Before..., T, After...> {
    using before = Tuple<Before...>;
    using after = Tuple<After...>;
};

关键问题是不知道Split模板该在哪一个T的位置对参数包进行分割。

17.7 规范化void

void是不完整类型,很多时候都需要单独考虑void

auto&& r = f();             // error if f() returns void
decltype(auto) r = f();     // error if f() returns void

17.8 为模板提供类型检查

虽然模板报错时的信息都比较长(说的就是你g++),但是却很难判断是模板定义出错了,还是用户代码触发实例化的代码出错了,C++准备引入concept来解决这个问题:

template<typename T> requires LessThanComparable<T>
T max(T a, T b)
{
    return b < a ? a : b;
}

struct X { };
bool operator> (X, X);

int main()
{
    X a, b;
    X m = max(a, b); // ERROR: X does not meet the LessThanComparable requirement
}

X不支持<运算符时,编译器将给出更简单的报错信息。

17.9 反射元编程(Reflective Metaprogramming)

反射指通过代码获取程序的信息,例如类的非静态数据成员:

template<typename T> void report(T p) {
    constexpr {
        std::meta::info infoT = reflexpr(T);
        for (std::meta::info : std::meta::data_members(infoT)) {
            -> {
                std::cout << (: std::meta::name(info) :) << ": " << p.(.info.) << '\n';
            }
        }
    }
    // code will be injected here
}

由于constexpr的存在,上述代码将在编译时计算,最终结果类似于下面的代码:

struct X {
    int x;
    std::string s;
};

template<> void report(X const& p) {
    std::cout << "x" << ": " << "p.x" << '\n';
    std::cout << "s" << ": " << "p.s" << '\n';
}

感觉是不能在运行时计算的,因为编译后的这些信息都丢掉了。这些信息类似于调试信息,如果带着的话可执行文件将变大。

17.10 参数包操作

目前只能通过sizeof...求出参数包中剩余参数个数,未来还可能支持下标运算来获取参数包中某个参数。

17.11 模块

主要用来提升编译速度,目前模板代码只能写在头文件中,虽然程序中可能只实例化很少的模板代码,但是却会对整个头文件进行词法分析和语法分析,引入模块后可以提前将头文件编译好以提升编译速度。

results matching ""

    No results matching ""