C ++错误:对`std :: cout`使用'::'无效

时间:2017-06-16 18:54:20

标签: c++ gcc g++ clang++

这可能是我在过去几年中看到的最奇怪的事情。

我有一个项目可以在两台完全不同的机器上完美构建(openSUSE Tumbleweed和ubuntu 14.04)。 我开始使用kubuntu 16.04的新机器,这个错误开始发生:

$ g++ -std=c++14 cout_qualif.cpp -lpng -o cout_qualif
In file included from cout_qualif.cpp:1:0:
debug_utils.h:19:19: error: invalid use of ‘::’
 # define msg std::cout

Clang也指出了一个错误,但是有一个完全不同的消息:

$ clang -std=c++14 cout_qualif.cpp -lpng -o cout_qualif
In file included from cout_qualif.cpp:3:
In file included from /usr/include/png++/png.hpp:34:
In file included from /usr/include/png.h:317:
/usr/include/zlib.h:94:19: error: non-friend class member 'cout' cannot have a qualified name
    z_const char *msg;  /* last error message, NULL if no error */
                  ^~~
./debug_utils.h:19:19: note: expanded from macro 'msg'
#       define  msg     std::cout
                        ~~~~~^
1 error generated.

我遇到的最简单的测试代码是:

#include <iostream>
#include "debug_utils.h"
#include <png++/png.hpp>

int main()
{
    msg << "Start" << std::endl;

    png::image< png::rgb_pixel > image("input.png");
    image.write("output.png");


    msg << "Finish" << std::endl;

    return 0;
}

和“debug_utils.h”:

#ifndef DEBUG_UTILS_H
#define DEBUG_UTILS_H

#include <iostream>

#   define  msg std::cout

#endif // DEBUG_UTILS_H

事实证明“png.h”包含“zlib.h”并且定义了一个结构:

typedef struct z_stream_s {
// ...
   z_const char *msg;  /* last error message, NULL if no error */

msg成员正在触发错误。如果我在#include "debug_utils.h"之后移动我的#include <png++/png.hpp>一行,那么一切似乎都能正常工作。

现在最后问题: 为什么这台机器无法编译我的代码而另外两个可以?

其他信息:

Kubuntu 16.04:
$ g++ --version
g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609

$ clang --version
clang version 3.8.0-2ubuntu4 (tags/RELEASE_380/final)

openSUSE Tumbleweed:
g++ is 7.1.1

Ubuntu 14.04:
Exact version not available at hand but I believe it is 4.9.x

1 个答案:

答案 0 :(得分:1)

重温问题的原因

#   define  msg std::cout
在包含debug_utils.h之后,debug_utils.h中的

std::cout替换为整个代码中msg的任何实例。由于msg是一个常见的短标识符,特别是对于消息缓冲区,因此无意中的替换始终是潜伏在代码中的风险。对此的解决方案显而易见:不要这样做。使用更长时间,不太可能重复替换或根本不做,并替换宏。在没有看到你的用例的情况下,我可能会用一个返回正确流的函数替换宏,编译器可以很容易地内联。

混淆和问题源于为什么宏替换中的错误仅在仅在三个候选PC中的一个上编译时由一段简单的测试代码产生。

答案是工具链和支持库的差异。出于这样或那样的原因,在这些PC中只有一个上,使用msg标识符的第三方标头包含在测试程序包含的第三方标头中。其他两个遵循不同的包含路径来构建相同的程序,并避免绊倒不需要的替换。