19.4 模仿强制
利用C++(www.cppentry.com)对成员函数形式的转换操作符的支持[Stro1997],我们可以写出行为像强制那样的类。此外,强制也可能涉及到语义上截然不同的类型。正如被广泛指出的那样[Meye1996, Meye1998, Dewh2003],对这种操作符的使用可能会导致许多问题,并且许多场合下它们都被误用了。但语言仍然选择支持它们,因为它们在某些场合下是非常有用的,例如把它们使用在强制类上面就是既合适又必需的。考虑如下的类,它将一个C风格的字符串转换为一个整型:
程序清单19.3
- class str2int
- {
- // 构造
- public:
- explicit str2int(char const *s)
- : m_value(atoi(s))
- {}
- // 操作符
- public:
- operator int() const
- {
- return m_value;
- }
- // 成员
- private:
- int m_value;
- };
现在,给出一个字符串,我们就可以通过如下形式将它"转型"为一个整型: - int val = str2int("34");
或者- int val = (str2int)"34";
或者甚至- int val = static_cast<str2int>("34");
尽管在本例中我们可以直接调用atoi()来达到目的,但上面的代码看起来确实有点酷,不是吗?借助于C++(www.cppentry.com)对模板的支持,该技术可以被泛化至相当可观的程度,从而带来令人愉快和惊讶的效果。
现在,考虑我们想要对这里的数值解析做一点扩展,并把它泛化到可以解析出所有整型(包括bool)的程度。显然,我们需要利用一点模板。有一点或许会令你感到惊讶,那就是最终的解决方案其实非常简单。(注意,之所以使用long long类型,仅仅是为了兼容于Metrowerks CodeWarrior。简单起见,我没有兼顾无符号类型或除char之外的字符编码。当然了,一个现实中的实现应该使用恰当的抽象,即int64_t,来提供一个更具可移植性的解决方案。)
程序清单19.4
- template <typename I>
- class str2int
- {
- // 构造
- public:
- explicit str2int(char const *s);
- // 操作符
- public:
- operator I() const
- {
- return m_value;
- }
- // 成员
- private:
- I m_value;
- };
-
- template <typename I>
- inline str2int<I>::str2int(char const *s)
- : m_value(static_cast<I>(atoi(s)))
- {}
- template <>
- inline str2int<long long>::str2int(char const *s)
- : m_value(strtoll(s, NULL, 10))
- {}
- template <>
- inline str2int<bool>::str2int(char const *s)
- : m_value(0 == (strcmp(s, "true"))
- {}
这个实现的一个令人愉快的副作用是它模拟了内建强制的形式。
- short s = str2int<short>("34");
- int i = str2int<int>("65536");
- bool b = str2int<bool>("true");
- long long ll = str2int<long long>("-9223372036854775808");
我们已经看到如何打扮成C++(www.cppentry.com)内建的强制操作符的样子。这固然很好,但还有其他实质性的地方吗?呃……我确信我们的方案对bool以及它的非数值形式的字符串表示(它可以被轻易地扩展以支持"1"和"TRUE"这样的字符串)引起了你的注意,但你可能想看到一些解决实质性问题的东西。毕竟,str2int类模板也可以仅仅被实现为一组相关的函数,如str2short、str2bool,等等。