我正在尝试编译以下代码,我在专用于 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.
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 COMMA ,
#define KK(x) x val;
void main()
{
KK(Test<int COMMA int>);
val.fun(12, 13);
}
我有一个新方法来解决这个麻烦。希望它可以帮助你:)
你有两个选择。其中之一已经提到过:使用
__VA_ARGS__
。然而,这有一个缺点,即它不能在严格的 C++03 中工作,而是需要充分兼容 C99/C++0x 的预处理器。
另一个选项是给类型名称加上括号。但与另一个答案不同的是,它并不像只将类型名称括起来那么简单。如下编写专业化是不正确的
// error, NOT valid!
template<> struct AClass<(int)> { X a; };
我已经通过在括号中传递类型名称来解决这个问题(并且 boost 可能在幕后使用相同的方法),然后从中构建一个函数类型
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 个(技术)问题:
BOOST_FOREACH
是一个众所周知的库,但他们唯一能做的就是告诉用户它的参数不应包含逗号,除非他们可以用括号括起来,但情况并非总是如此>>
符号,而该符号将无法正确解析。有预处理/模板元编程技巧,但是更简单的解决方案是使用不带逗号的类型:
typedef std::vector<int, std::allocator<int> > FooVector;
specialize_AClass(FooVector)
最后,还有一个美学问题,由于宏的普遍性,宏最好是不能与“常规”(类型、函数、变量)名称冲突的名称。共识通常是使用所有大写标识符,例如:
SPECIALIZE_ACLASS
请注意,这不能以下划线开头,因为标准限制使用与
_[A-Z].*
或 [^_]*__.*
匹配的标识符给标准库的编译器编写者或他们喜欢的任何东西(这些不是笑脸 :p)
由于预处理器在语义分析之前运行,因此模板参数中的逗号将被解释为宏的参数分隔符。相反,您应该能够使用可变参数宏来执行以下操作:
#define specialize_AClass(...)\
template<> struct AClass< __VA_ARGS__ > { X a; };
如果您愿意在调用宏之前添加更多代码,您始终可以这样做作为解决方法:
typedef std::vector<int,std::allocator<int> > myTypeDef;
specialize_AClass(myTypeDef) //works
#define EMPTY()
#define DEFER( ... ) __VA_ARGS__ EMPTY()
specialize_AClass( DEFER (std::vector<int,std::allocator<int> >) )
对于简单的事情,您可以使用
typedef
#include <vector>
template<typename A>
struct AClass {};
#define specialize_AClass(X)\
template<> struct AClass<X> { X a; };
specialize_AClass(int) //ok
typedef std::vector<int,std::allocator<int>> AllocsVector;
specialize_AClass(AllocsVector) //ok
int main()
{
return 0;
}
您的代码还有很多其他问题,但为了解决特定问题,预处理器仅将
<
和 >
视为小于和大于运算符。
这就是它对 C++ 的了解程度。
可以使用一些技巧来允许模板表达式作为宏参数传递,但对于初学者来说,简单且极大的最佳答案是:
不要那样做。
干杯,