宏中使用的模板参数出现问题

问题描述 投票:0回答:8

我正在尝试编译以下代码,我在专用于 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

c++ templates parameters macros
8个回答
16
投票
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);
}

我有一个新方法来解决这个麻烦。希望它可以帮助你:)


12
投票

你有两个选择。其中之一已经提到过:使用

__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; 
};

您只需要传递用括号括起来的类型。


8
投票

这里有几个问题。

首先,宏非常愚蠢,它们很复杂,但本质上相当于纯文本替换过程。

因此,您公开的代码存在 2 个(技术)问题:

  1. 你不能在宏调用中间使用逗号,它只会失败,
    BOOST_FOREACH
    是一个众所周知的库,但他们唯一能做的就是告诉用户它的参数不应包含逗号,除非他们可以用括号括起来,但情况并非总是如此
  2. 即使发生替换,您的代码在 C++03 中也会失败,因为它会在模板特化末尾创建一个
    >>
    符号,而该符号将无法正确解析。

有预处理/模板元编程技巧,但是更简单的解决方案是使用不带逗号的类型:

typedef std::vector<int, std::allocator<int> > FooVector;
specialize_AClass(FooVector)

最后,还有一个美学问题,由于宏的普遍性,宏最好是不能与“常规”(类型、函数、变量)名称冲突的名称。共识通常是使用所有大写标识符,例如:

SPECIALIZE_ACLASS

请注意,这不能以下划线开头,因为标准限制使用与

_[A-Z].*
[^_]*__.*
匹配的标识符给标准库的编译器编写者或他们喜欢的任何东西(这些不是笑脸 :p)


3
投票

由于预处理器在语义分析之前运行,因此模板参数中的逗号将被解释为宏的参数分隔符。相反,您应该能够使用可变参数宏来执行以下操作:

#define specialize_AClass(...)\
template<> struct AClass< __VA_ARGS__ > { X a; };

1
投票

如果您愿意在调用宏之前添加更多代码,您始终可以这样做作为解决方法:

typedef std::vector<int,std::allocator<int> > myTypeDef; 
specialize_AClass(myTypeDef) //works

1
投票
#define EMPTY()

#define DEFER( ...  ) __VA_ARGS__  EMPTY()

specialize_AClass( DEFER (std::vector<int,std::allocator<int> >) )

0
投票

对于简单的事情,您可以使用

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;
}

-3
投票

您的代码还有很多其他问题,但为了解决特定问题,预处理器仅将

<
>
视为小于和大于运算符。

这就是它对 C++ 的了解程度。

可以使用一些技巧来允许模板表达式作为宏参数传递,但对于初学者来说,简单且极大的最佳答案是:

不要那样做。

干杯,

© www.soinside.com 2019 - 2024. All rights reserved.