假设有一个,为了解析的目的,想要跳过模板标识符列表的内容:
template<(valid code)>
^ ^
| from | to
首先想到的是盲目地找到第一个&gt;,但这并不总是有效:
template<(valid code),template<(valid code)> >
^ ^
| from | to, oops
更好的方法是跳过成对的&lt;和&gt;递归:
template<(valid code),template<(valid code)> >
^ ^
| from | to, better
然而,即使这种方法对于像这样的奥术但有效的杰作也是失败的(来自bits \ random.h,第69行,GCC 4.7.x):
template<(...),bool = __w < static_cast<size_t>(...)>
^ ^ ^ ^ ^ ^
| 1 | 2 | 3 | 2 | 1 | where did 0 go?
我的问题是,找到任何有效模板标识符列表结尾的正确方法是什么?
答案 0 :(得分:2)
找到任何有效的模板标识符列表的结尾真的没有简单的方法,因为除了bits/random.h
的示例之外,还有更多的病态可能性。 (在这个答案的最后有一个病态案例:Is C++ context-free or context-sensitive?,其中标识符是否是模板,取决于较大的整数是否为素数。)
该算法易于说明(在C ++ 11标准的第14.2节第3段[temp.names]
中)。基本上,<
是模板括号,当且仅当它跟随模板标识符,单词template
或其中一个cast
运算符时。一旦打开<
,它就会与第一个>
匹配,而第一个>
不会嵌套在某些括号语法(包括大括号和括号)中,并且与某些嵌套模板括号不匹配。在C ++ 11中,这包括>>
,它是random.h
令牌的一部分。
例如,如果bool = __w > ...
示例说>
,则<
将被视为结束模板括号。但由于它是__w
且<
不是模板标识符,因此将其视为小于运算符。恕我直言的好风格总是用括号中的比较和移位运算符包装,如果它们在模板括号内,但那只是我。
标准的准确措辞:
名称查找后发现名称是模板名称或 operator-function-id 或文字operator-id 是指一组重载函数,如果后跟
<
,则其中任何成员都是函数模板,>
始终被视为 template-argument-list <的分隔符/ em>并且从不作为小于运营商。解析 template-argument-list 时,第一个非嵌套{{1}}被视为结束分隔符而不是大于运算符。
该算法难以实现,因为没有简单的方法可以知道哪些标识符是模板标识符,因为您需要解析整个程序(至少在使用点之前)以了解每个标识符是什么类型的。这包括查找和解析包含的文件,以及预处理源代码。
如果你真的希望它是准确的,你需要使用C ++解析器并注意它可能是一个非常缓慢的过程,因为它可以包括模板实例化(与引用的prime-check示例一样)。如果你只是尝试做类似语法着色的事情,你可能会得到近似值。
答案 1 :(得分:1)
您需要跳过()
括号内的任何内容
您需要在<
之前查看标识符,并知道它是否是类型名称。
第二个是有问题的,您需要扫描所有源代码并识别class/struct/union
和typedef
的所有名称,以便在到达表单{ {1}}(此示例已简化)您知道__w < __a
是否为某个类型命名
这有问题的原因是如果您遇到任何预处理器元编程(如Boost的主题库),您基本上不会编写一个程序,可以对这些程序进行评估以查看创建的类型名称。
此外,请考虑这段代码(稍微复杂一点,以显示它有多难):
__w
这种复杂性是 template <template <class> class T>
struct Base
{
template <class X>
using type = T<X>;
};
template <>
struct Base<std::numeric_limits>//Let's assume numeric_limits only has one template argument for this example
{
static const int type = 0;
};
template <class T, Base<T>::type < 0>
struct OtherClass{};
关键字的用途,因为typename
是一个依赖范围,您无法立即判断Base<T>
是否为类型或成员变量命名。通过这种方式,语法要求Base<T>::type
是一个成员变量而Base<T>::type
是一个类型(我们不知道哪种类型,但是没关系,我们只是试图找到结束模板标识符列表)
除上述示例之外,您还需要处理typename Base<T>::type
关键字的鲜为人知的使用。由于template
是一个依赖范围,您如何解析Base<T>
?这取决于Base<T>::type<0>
是什么。在这种情况下,语法要求type
是成员变量并被解析为Base<T>::type
,而(Base<T>::type < 0) >
是模板表达式。
关键字Base<T>::template type<0>
和typename
虽然在了解相关范围之前难以理解,但最终会让您的工作更轻松。否则,如果没有编写完整的编译器,模板标识符列表的结尾几乎无法回答的简单问题(即使编译器编写起来也更难)。
除了依赖范围之外,您仍然需要能够处理非依赖范围。这意味着您需要知道template
是否是类型,这意味着扫描每个Base<numeric_limits>::type
struct/class/union
并解析基类typedef
和私有继承的公共继承+ typedef
陈述。
如果你限制自己编写的代码,你的工作仍然易于处理,否则你需要做更多的工作。
我不保证这就是一切,只是这很可能会让你忙碌一段时间。