C++ 高级元编程(十)
十三、附录 A:练习
A.1 .练习
在以下所有问题中,读者应该假设给出了一个带有一些模板代码的可写文件;可以添加更多的文件,项目的其余部分是只读的。
A.1.1 .延期
给出了一个函数模板:
template <typename T>
void f(T x)
{
printf("hello T");
}
- 添加另一个重载,为从 BASE 派生的每个类调用该重载,并打印“hello BASE-or-derived”
- 确保您的解决方案是可靠的。把 f 的返回类型改成 int,看看你的解是否还成立
- 确保您的解决方案是可靠的。在同一个文件中添加一个普通函数——比如 int f(double x)——看看编译是否失败
- 想一个替代方案,最大限度地减少对现有代码的更改。
A.1.2 .整数
以下代码:
template <typename T>
uint32 f(T x) { ... }
// ...
printf("%x", f(a));
发出警告:f 的返回类型与%x 不兼容。
你会进行什么样的调查?
A.1.3 .日期格式
遵循第 7.7.1 节,使用更加自然的语法实现常量生成器,例如:
YYYYMMDD = dateformat<'Y','Y',Y','Y','M','M','D','D'>::value
或者
YYYYMMDD = dateformat<'Y','Y',Y','Y','/','M','M','/','D','D'>::value
A.1.4 .专业化
给出了一个模板类:
template <typename T>
class X
{ /* very long implementation... */ };
修改 X,使 X 正好有一个额外的数据成员(比如 int)和一个额外的成员函数。对现有代码进行最小的更改(因此,如果源文件在版本控制软件下,差异是不言自明的)。
A.1.5 .位计数
下面的代码:
// returns the number of bits of base
template <size_t BASE>
struct nb
{
static const size_t value
= nb<BASE % 8>::value
+ nb<(BASE/8) % 8>::value + nb<BASE/16>::value;
};
template <> struct nb<0> { static const size_t value = 0; };
template <> struct nb<1> { static const size_t value = 1; };
template <> struct nb<2> { static const size_t value = 1; };
template <> struct nb<3> { static const size_t value = 2; };
template <> struct nb<4> { static const size_t value = 1; };
template <> struct nb<5> { static const size_t value = 2; };
template <> struct nb<6> { static const size_t value = 2; };
template <> struct nb<7> { static const size_t value = 3; };
- 是完全正确的,它展示了一种在本书(或任何其他同等的书)中没有见过的新技术
- 有一个小错误,但该技术可与 3.6.6 节及以后的章节相媲美
- 至少有一个不小的错误,不容易修复
A.1.6 .质数
作为调试技术的一个练习,我们给出一个非平凡元函数 is _ prime:::value 的例子。
读者应该能够理解代码,至少在原则上,即使一些算法细节是未知的。
#define mxt_EXPLICIT_VALUE(CLASS, TPAR, VALUE) \
template <> struct CLASS<TPAR> { static const size_t value = VALUE; }
template <size_t N>
struct wheel_prime;
mxt_EXPLICIT_VALUE(wheel_prime, 0, 7);
mxt_EXPLICIT_VALUE(wheel_prime, 1, 11);
mxt_EXPLICIT_VALUE(wheel_prime, 2, 13);
mxt_EXPLICIT_VALUE(wheel_prime, 3, 17);
mxt_EXPLICIT_VALUE(wheel_prime, 4, 19);
mxt_EXPLICIT_VALUE(wheel_prime, 5, 23);
mxt_EXPLICIT_VALUE(wheel_prime, 6, 29);
mxt_EXPLICIT_VALUE(wheel_prime, 7, 31);
template <size_t A>
struct nth_tentative_prime
{
static const size_t value
= 30*((A-3)/8) + wheel_prime<(A-3) % 8>::value;
};
mxt_EXPLICIT_VALUE(nth_tentative_prime, 0, 2);
mxt_EXPLICIT_VALUE(nth_tentative_prime, 1, 3);
mxt_EXPLICIT_VALUE(nth_tentative_prime, 2, 5);
template
<
size_t A,
size_t N,
size_t K = nth_tentative_prime<N>::value,
size_t M = (A % K)
>
struct is_prime_helper
{
static const bool EXIT = (A < MXT_M_SQ(K));
static const size_t next_A = (EXIT ? 0 : A);
static const size_t next_N = (EXIT ? 1 : N+1);
};
template <size_t A, size_t N, size_t K>
struct is_prime_helper<A, N, K, 0>
{
static const size_t next_A = 0;
static const size_t next_N = 0;
};
template <size_t A, size_t N = 0>
struct is_prime
: is_prime<is_prime_helper<A, N>::next_A,
is_prime_helper<A, N>::next_N>
{
};
template <> struct is_prime<0,0> { static const bool value = false; };
template <> struct is_prime<0,1> { static const bool value = true; };
template <> struct is_prime<1,0> { static const bool value = true; };
template <> struct is_prime<2,0> { static const bool value = true; };
A.1.7 .不带 RTTI 的 Typeinfo
5.3.2 节中的 typeinfo 包装器依赖于编译器来为不同的类型生成运行时标识符。如果这不可用,那么可以使用不同的实现(至少在某些情况下):
- 创建一个 traits 类 TI ,它有一个返回 T()的静态成员函数 T f()
- 使用 reinterpret_cast 并将&TI ::f 转换为 void (*)()
- 使用后一个指针作为 std::map 中的索引
- 证明这是可行的(提示:由于 ICF,步骤#1 是必要的,参见第 354 页;对于步骤#3,请参见标准的第 20.3.3 节)
- 请注意,基于指针的类型标识符使用静态类型,而 typeinfo 使用动态类型,因此这种技术通常较弱。
A.1.8 .提示和部分解决方案
由于练习#1 的实际重要性,我们给出了它的解决方案。
显然单靠超载是不行的。我们可以选择 BASE,但 a DERIVED 会更喜欢模板函数(T=DERIVED,这是一个精确匹配)。
template <typename T>
void f(T x)
{
printf("hello T");
}
void f(BASE& x)
{
printf("hello BASE");
}
相反,我们引入了另一层:
template <typename T>
void f(T x)
{
g(&x, &x);
}
template <typename T>
void g(T* p, void*)
{
printf("hello T");
}
template <typename T>
void g(T* p, BASE*)
{
printf("hello BASE-OR-DERIVED");
}
优选将 T转换为 BASE。
注意,同样的技术也解决了另一个问题;当调用成员函数的参数时,我们可以防止虚调用:
template <typename T>
void g(T* p, void*)
{
printf("Not a BASE. No call was made.");
}
template <typename T>
void g(T* p, BASE* b)
{
b->doit(); // always virtual, if BASE::doit is virtual
p->doit(); // may be virtual or not
p->T::doit(); // always non-virtual
}
注意原则上 T 可能隐藏 BASE::doit,所以第二个调用不会是虚的:
class BASE
{
public:
virtual void doit();
};
class D1 : public BASE
{
public:
void doit(int i = 0);
};
class D2 : public D1
{
public:
virtual void doit();
};
十四、附录 B:参考文献
[1] Alexandrescu A .,《现代 C++ 设计》,Addison-Wesley
[2]d . Vandevoorde 和 and Josuttis,“C++ 模板:完全指南”,Addison-Wesley
[3] Abrahams D .和 Gurtovoy A .,“C++ 模板元编程”,Addison-Wesley
[4] Sutter H .,“异常的 C++ 风格”,Addison-Wesley
[5]威尔逊,《不完美的 C++》,艾迪森-卫斯理
[6] Austern M .,“泛型编程和 STL”,Addison-Wesley
[7] Cline M .,“C++ FAQ (lite)”,www . cs . rit . edu/~ mj/docs/c++ FAQ/
[8]迈耶斯,“有效短期债务”
[9] Coplien,j .,“奇怪地重复出现的模板模式”,C++ 报告,1995 年 2 月,第 24-27 页。
[10] Stroustrup,b .,“C++ 的设计与进化”,Addison-Wesley,雷丁,MA,1993。
[11] Barton,J.J .和 Nackman L.R .,“科学与工程 C++”,Addison-Wesley,雷丁,MA,1994 年。
[12]t . Veldhuizen,“表达式模板”,C++ 报告,1995 年 6 月,在 Stanley Lippman 编辑的 C++ Gems 中重印。
[13] Myers Nathan C .,“一种新的和有用的模板技术:特征”,C++ 报告,1995 年 6 月,www.cantrip.org/traits.html
[14] C++ 0x (C++11)最终委员会草案:
ansi.org 的 c++ 11–ISO/IEC 14882:2011:60 美元
[15] Meyers S .,有效的现代 C++,O'Reilly,2014
[16] Meucci,a .“风险和资产分配”,Springer 2005 年
第一部分:先决条件
#incluse<prerequisites>
第二部分:技巧
#include<techniques>
第三部分:应用
#include<applications>