Проблема с параметрами шаблона, используемыми в макросах
Я пытаюсь скомпилировать следующий фрагмент кода, я получаю сообщение об ошибке в строке, которая специализируется на std::vector, кажется, что один передаваемый параметр как-то считается двумя параметрами. Может быть, это что-то связано с угловыми скобками?
Есть ли особый способ/механизм, по которому такие параметры могут быть правильно переданы макросу?
#include <vector>
template<typename A>
struct AClass {};
#define specialize_AClass(X)\
template<> struct AClass<X> { X a; };
specialize_AClass(int) //ok
specialize_AClass(std::vector<int,std::allocator<int> >) //error
int main()
{
return 0;
}
Ошибка, которую я получаю, следующая:
1 Line 55: error: macro "specialize_AClass" passed 2 arguments, but takes just 1
2 Line 15: error: expected constructor, destructor, or type conversion before 'int'
3 compilation terminated due to -Wfatal-errors.
Ссылка: http://codepad.org/qIiKsw4l
Ответы
Ответ 1
У вас есть два варианта. Один из них уже упоминался: Использование __VA_ARGS__
. Это, однако, имеет тот недостаток, что он не работает в строгом С++ 03, но требует достаточно C99/С++ 0x совместимого препроцессора.
Другой вариант - заключить в скобки имя типа. Но в отличие от другого ответа, это не так просто, как просто вставить в скобки имя типа. Написание специализации следующим образом плохо сформировано
// error, NOT valid!
template<> struct AClass<(int)> { X a; };
Я работал над этим (и, вероятно, увеличил его значение под капотом), передав имя типа в круглых скобках, а затем создав из него тип функции
template<typename T> struct get_first_param;
template<typename R, typename P1> struct get_first_param<R(P1)> {
typedef P1 type;
};
При этом get_first_param<void(X)>::type
обозначает тип X
. Теперь вы можете переписать свой макрос на
#define specialize_AClass(X) \
template<> struct AClass<get_first_param<void X>::type> {
get_first_param<void X>::type a;
};
И вам просто нужно передать тип, заключенный в круглые скобки.
Ответ 2
template<typename TypeX, typename TypeY>
class Test
{
public:
void fun(TypeX x, TypeY y)
{
std::wcout << _T("Hello") << std::endl;
std::wcout << x << std::endl;
std::wcout << y << std::endl;
}
};
#define COMMOA ,
#define KK(x) x val;
void main()
{
KK(Test<int COMMOA int>);
val.fun(12, 13);
}
У меня есть новый способ решить эту проблему. надеюсь, что это может вам помочь:)
Ответ 3
Здесь есть пара проблем.
Прежде всего, макросы очень тупые, они сложны, но в основном это чистый процесс замены текста.
Таким образом, существуют 2 (технические) проблемы с кодом, который вы указали:
- Вы не можете использовать запятую в середине вызова макроса, она просто терпит неудачу,
BOOST_FOREACH
- это хорошо известная библиотека, и все же единственное, что они могут сделать, это сказать пользователю, что аргументы не должны содержать запятых, если они не могут быть завернуты в скобки, что не всегда так.
- Даже если замена произошла, ваш код не сработает в С++ 03, потому что он создаст символ
>>
в конце специализации шаблона, который не будет правильно проанализирован.
Есть препроцессоры/метапрограммирование шаблона, однако более простым решением является использование типа без запятых:
typedef std::vector<int, std::allocator<int> > FooVector;
specialize_AClass(FooVector)
Наконец, существует эстетическая проблема, из-за их повсеместности макросы являются лучшими именами, которые не могут столкнуться с именами "регулярных" (типов, функций, переменных). Консенсус обычно заключается в использовании всех идентификаторов верхнего регистра, например:
SPECIALIZE_ACLASS
Обратите внимание, что это не может начинаться с подчеркивания, поскольку стандарт ограничивает использование идентификаторов, соответствующих _[A-Z].*
или [^_]*__.*
, для авторов компилятора для стандартной библиотеки или того, что они считают (это не смайлики: p)
Ответ 4
Поскольку препроцессор работает до семантического анализа, запятая в вашем параметре шаблона интерпретируется как разделитель аргументов для макроса. Вместо этого вы должны иметь возможность использовать переменные макросы, чтобы сделать что-то вроде этого:
#define specialize_AClass(...)\
template<> struct AClass< __VA_ARGS__ > { X a; };
Ответ 5
Если вы хотите добавить немного больше кода перед вызовом своего макроса, вы всегда можете сделать это как обходной путь:
typedef std::vector<int,std::allocator<int> > myTypeDef;
specialize_AClass(myTypeDef) //works
Ответ 6
Есть много других проблем с вашим кодом, но для решения конкретного вопроса препроцессор просто обрабатывает <
и >
как меньше и больше, чем операторов.
Что степень его знаний о С++.
Есть несколько трюков, которые можно использовать для разрешения шаблонных выражений в качестве аргументов макроса, но простой и чрезвычайно большой край наилучший ответ для новичков:
НЕ ДЕЛАЙТЕ ЭТО.
Приветствия и hth.,