Странное поведение с шаблонами и #defines - PullRequest
0 голосов
/ 06 июля 2010

У меня есть следующие определения:

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 ]

6 голосов
/ 06 июля 2010

Запятая внутри макроса может быть неоднозначной: дополнительный набор скобок (ваш второй пример) является одним из способов устранения неоднозначности. Рассмотрим макрос

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

тогда вы могли бы написать VERIFY( foo<bar, x> y ).

Другой способ устранения неоднозначности - с

typedef Test1<int,int> TestII;
VERIFY_R( TestII::hello() );
5 голосов
/ 06 июля 2010

Препроцессор - это инструмент для замены текста, который ничего не знает о C ++.Он интерпретирует

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

как

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

, что вызывает VERIFY_R со слишком большим количеством параметров.Как вы заметили, дополнительные скобки исправляют это:

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

Однако остается вопрос, зачем вам нужен препроцессор.Макрос, который вы использовали в своем вопросе, также может быть функцией inline.Если ваш реальный код ничего не делает, требуя препроцессора, попробуйте избавиться от макросов.Они просто причиняют боль.

4 голосов
/ 06 июля 2010

Запятая в <int, int> рассматривается как разделитель аргументов для макроса, а не для шаблона.Поэтому компилятор считает, что вы вызываете VERIFY_R с двумя аргументами (Test1<int и int>::hello()), когда для этого требуется только один.Вам нужно использовать variadic macros , чтобы развернуть все, что предоставляется макросу:

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

Обычно хорошей идеей является также заключать аргументы макроса в круглые скобки, чтобы предотвратить другие видыстранные ошибки замещения.

3 голосов
/ 06 июля 2010

Препроцессор не знает, что < и > должны быть в скобках, поэтому он интерпретирует выражение как два макро аргумента, Test1<int и int>::hello(), разделенные ,. Как вы говорите, это можно исправить, окружив все выражение скобками, которые препроцессор распознает в скобках.

0 голосов
/ 06 июля 2010

Я не уверен, является ли это ошибкой в ​​ваших отчетах или фактической проблемой, но ваш последний VERIFY_R все еще ссылается на Test1, а не Test2.

...