我在做家庭作业时注意到,笔记本电脑上的编译器比我们期望用于提交的机器上的编译器要宽松得多。我的笔记本电脑上的C++编译器是AppleClang 7.0.2.7000181
,提交框上的编译器是g++ 4.9.2
。回想起来,不应该编译的代码是:
#include <iostream>
std::tuple<int, int> foo() {
return std::make_tuple(1, 1);
}
int main() {
auto pair = foo();
int x = std::get<0>(pair);
int y = std::get<1>(pair);
std::cout << x << "," << y << std::endl;
return 0;
}
我还有一个CMakeLists.txt:
cmake_minimum_required(VERSION 2.8)
project(foo)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -Wall -Werror -Werror=sign-compare")
set(SOURCE_FILES main.cpp)
add_executable(foo ${SOURCE_FILES})
在我的笔记本电脑上,Clang愉快地编译了这段代码并打印出1,1
。没有错误,没有警告,什么都没有。在提交框上,我就没那么幸运了。
/home/nate/foo/main.cpp: In function 'std::tuple<int, int> foo()':
/home/nate/foo/main.cpp:3:26: error: return type 'class std::tuple<int, int>' is incomplete
std::tuple<int, int> foo() {
^
/home/nate/foo/main.cpp:4:12: error: 'make_tuple' is not a member of 'std'
return std::make_tuple(1, 1);
^
/home/nate/foo/main.cpp: In function 'int main()':
/home/nate/foo/main.cpp:8:21: error: 'void pair' has incomplete type
auto pair = foo();
^
/home/nate/foo/main.cpp:9:13: error: 'get' is not a member of 'std'
int x = std::get<0>(pair);
^
/home/nate/foo/main.cpp:10:13: error: 'get' is not a member of 'std'
int y = std::get<1>(pair);
^
这些错误是有道理的,因为我没有包含tuple
标头,但我不明白为什么这段代码在我的笔记本电脑上编译。这是怎么回事?
不同的编译器有不同的头文件。C++头文件与你正在使用的编译器密切相关。
你可能知道的是,无论出于何种原因,Clang的<iostream>
本身#include
元组头文件;因此,引用std::tuple
的代码只需#include
<iostream>
头文件即可愉快地编译。
当然,这将依赖于编译器的特定行为。
不同的编译器具有不同的头文件。C++标准不禁止一个头文件自动包含另一个头文件;但是,为了使用特定的类、模板或其他资源,确保此类、模板或其他资源可供代码引用的唯一方法是显式包含其头文件。
不同的实现可能会选择在其他标头中包含一些标头。应始终包含使用的标头。如果你这样做了,那么它将使用两个编译器进行编译。似乎 clang 在<iostream>
中包含<tuple>
,而 gcc 则没有。无论哪种方式,他们都可以这样做,您必须确保包含所需的标头。
允许使用标准标头,但不需要包含其他标准标头。clang 的iostream
标头很可能直接或间接包含tuple
。没有标准机制可以诊断您是否因此忘记了标准标头。
代码由特定编译器编译的事实并不意味着您的代码是正确的。如文档中所述,std::tuple
它需要包含标头<tuple>
,违反该规定会使您的代码不正确,尽管编译器没有义务检测它并提供诊断。