编译一个利用f2c.h头文件的基于C ++的项目,该头文件中的min和max宏定义如下所示...
#ifndef min
#define min(a,b) ((a) <= (b) ? (a) : (b))
#endif
#ifndef max
#define max(a,b) ((a) >= (b) ? (a) : (b))
#endif
当我使用Microsoft Visual Studio在Windows上编译并运行时,此代码没有问题。现在,我尝试使用g ++编译器进行编译并在LINUX上运行,并且收到以下错误消息...
In file included from tsdriver.cpp:68:0:
/usr/include/c++/6/bits/fstream.tcc: In member function ‘virtual
std::basic_filebuf<_CharT, _Traits>::int_type std::basic_filebuf<_CharT,
_Traits>::underflow()’:
f2c.h:155:18: error: expected unqualified-id before ‘(’ token
#define min(a,b) ((a) <= (b) ? (a) : (b))
现在,它只给我有关min宏的错误消息,但是我认为这是由于编译器错误所致,因此必须终止编译。
我已经进行了一些测试,发现可以从一个小的test.cpp程序中调用min / max,而没有任何额外的包含,因此最初认为错误是来自重新定义的,但这没关系,因为#ifndef会防止这种情况?
我不确定如何解决这个问题,并且我仍在适应LINUX开发环境。任何帮助表示赞赏。
谢谢。
答案 0 :(得分:2)
恭喜,您刚刚了解了macros are evil的使用方法;)宏的问题在于,它们实际上不仅仅只是一种自动文本替换机制,它可以在“实际”编译器获取代码之前更改源代码。看见。它们一般不尊重诸如名称空间或范围之类的东西,因为它们在甚至还不存在这些概念的级别上运行。 C ++从C继承了宏。但是,有了constexpr
,inline
和模板,C ++可以更好地解决大多数您通常在C中使用宏的问题。尽管可以说有一些对于C ++中宏的合法用例,通常最好避免使用宏。
那么用g ++编译代码时发生了什么?好吧,fstream.tcc的第396行是您正在使用的标准库实现的一部分,如下所示:
__ilen = std::min(__avail, __buflen);
您很可能包含了一部分标准库,而该库间接包含了fstream.tcc。您很可能在标头中包含了宏定义
#define max(a,b) ((a) >= (b) ? (a) : (b))
,包括该标准标头。这意味着当编译器通过fstream.tcc时,您已经将max
定义为类似于函数的宏。因此,编译器将相应地替换所有与max
(
…,
…)
匹配的令牌序列。这意味着代替
__ilen = std::min(__avail, __buflen);
您的标准标题突然显示为:
__ilen = std::((__avail) >= (__buflen) ? (__avail) : (__buflen))(__avail, __buflen);
// ^--- note this part
这也解释了奇怪的错误消息:
在'('令牌之前
std::(
是语法错误。编译器期望::
之后的另一个标识符。最初只有一个。但是宏将该标识符替换为垃圾。因为所有宏都在不考虑语法上下文的情况下替换了令牌。而且由于宏只是为您静默地重写代码,因此您永远也看不到“实际”编译器看到的代码。当出现问题时,您将看到的是重写的代码所产生的错误消息。 (除非您告诉编译器,请转储预处理结果,然后在结果文本中进行筛选。)
解决此问题的最佳方法:摆脱宏,而仅使用std::min()
和std::max
。如果这样做不那么容易,至少应该至少可以将这些宏转换为内联函数。