模板和#defines的奇怪行为

时间:2010-07-06 12:44:53

标签: c++ templates visual-studio-2008 syntax c-preprocessor

我有以下定义:

template<typename T1, typename T2>
class Test2
{
public:
    static int hello() { return 0; }
};

template<typename T>
class Test1
{
public:
    static int hello() { return 0; }
};

#define VERIFY_R(call) { if (call == 0) printf("yea");}

有了这些,我尝试编译以下内容:

VERIFY_R( Test1<int>::hello() ); 

这个编译很好

VERIFY_R( (Test2<int,int>::hello()) );

这也编译得很好,注意电话周围的括号。

VERIFY_R( Test2<int,int>::hello() );

如果没有括号,会产生警告和几个语法错误:

warning C4002: too many actual parameters for macro 'VERIFY_R'
error C2143: syntax error : missing ',' before ')'
error C2059: syntax error : ')'
error C2143: syntax error : missing ';' before '}'
error C2143: syntax error : missing ';' before '}'
error C2143: syntax error : missing ';' before '}'
fatal error C1004: unexpected end-of-file found    

这里发生了什么事? 这种情况发生在VS2008 SP1上。

5 个答案:

答案 0 :(得分:6)

宏中的逗号可能不明确:一组额外的括号(第二个示例)是一种消除歧义的方法。考虑一个宏

#define VERIFY(A, B) { if ( (A) && (B) ) printf("hi"); }

然后你可以写VERIFY( foo<bar, x> y )

另一种消除歧义的方法是使用

typedef Test1<int,int> TestII;
VERIFY_R( TestII::hello() );

答案 1 :(得分:5)

预处理器是一个愚蠢的文本替换工具,对C ++一无所知。它解释了

VERIFY_R( Test1<int,int>::hello() );

as

VERIFY_R( (Test1<int), (int>::hello()) );

使用太多参数调用VERIFY_R。如您所述,其他括号修复此问题:

VERIFY_R( (Test1<int,int>::hello()) );

然而,问题仍然是为什么你需要预处理器。您在问题中使用的宏也可以是inline函数。如果您的实际代码不执行任何需要预处理器的操作,请尝试删除宏。他们只会引起疼痛。

答案 2 :(得分:4)

<int, int>中的逗号被视为宏的参数分隔符,而不是模板。因此,编译器认为您使用两个参数(VERIFY_RTest1<int)调用int>::hello(),只需要一个参数。您需要使用variadic macros来扩展提供给宏的所有内容:

#define VERIFY_R(...) { if ((__VA_ARGS__) == 0) printf("yea");}

通常,在括号中包装宏参数也是一个好主意,以防止其他类型的奇怪替换错误。

答案 3 :(得分:3)

预处理器不知道<>应该是括号,因此它将表达式解释为两个宏参数Test1<intint>::hello(),分开由,。如您所说,可以通过用括号括起整个表达式来修复它,预处理器将其识别为括号。

答案 4 :(得分:0)

我不确定这是您的报告中的错误还是实际问题,但您的上一次VERIFY_R仍然是引用Test1而不是Test2。