C---高级元编程-十-

39 阅读5分钟

C++ 高级元编程(十)

原文:Advanced Metaprogramming in Classic C++

协议:CC BY-NC-SA 4.0

十三、附录 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 D1public BASE
{
public:
   void doit(int i = 0);
};

class D2public 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>