C++编译错误测试

5
我是一名学生,正在尝试编写和运行一些测试代码来检查作业是否正确。现在我正在尝试测试我的代码是否正确地防止值语义。在我的作业中,对于每个类,我声明了它自己的私有复制构造函数和赋值运算符,但没有定义,因此什么也不做。当它们在我的测试程序中被调用时,我得到了像我预期的编译错误。就像这样:

error: 'myClass::myClass(const &myClass)' is private'

error: 'myClass& myClass::operator=(const myClass&)' is private

是否有一种方法可以使用try/catch,使我的测试代码能够编译和运行,但显示这些错误确实发生了吗?我已经尝试过:
myClass obj1(...);
myClass obj2(...);
try{
  obj1 = obj2;
  throw 1;
}
catch(int e){
  assert(e==1);
}

但编译器仍然给我上述错误。 这些不是“异常”吗?它们不会触发抛出吗?
如果我正确理解try/catch,它处理运行时错误,而不是我之前遇到的那种错误,对吗?
经过更多研究,似乎没有(简单的)方法在C++中本地测试某些编译错误(现在我想想,这可能适用于大多数语言)。 我读了一个post,建议使用脚本语言编写一些测试代码,尝试编译C++代码片段并检查任何错误,另一个post则建议使用Boost.Build。
做我正在努力做的事情的最简单/最好方法是什么?
我查看了Boost.Build的文档,但有点超出我的理解范围。 如果我使用它,我该如何测试一个文件,比如'test.cpp'编译,并处理发生在'test.cpp'的特定编译错误?
谢谢你的帮助!

附言:这是我发布的第一篇文章之一,希望我已经做了“足够”的研究并且做好了其他所有的事情。如果我没有做到,请原谅。


一旦编译程序,您就会得到一个错误列表。这还不够吗?!如果您添加一个测试类,它可以从您的代码中提取更多的“编译错误”。测试类是编写用于(在编译后)运行您的代码并提取语义错误的。 - Shahbaz
在这种情况下,我只想测试一小部分操作,阅读编译错误并不是什么大问题。但是当你在处理一个大项目时,有许多操作/行为需要避免,你希望产生编译错误的操作/行为呢?手动逐个检查似乎有点繁琐。 - Jared Roder
等等,你不能为运行时操作/行为“产生编译错误”。编译器读取你的代码,确保它在语法、词汇和语义上符合C++语言,并为其生成代码。它只检查C++语言规定的内容。如果生成的代码不起作用,那就与编译器无关(因此它不能给你编译错误)。 - Shahbaz
在大型项目中编译错误也不是什么大问题。首先,您始终会在开发过程中进行编译,因此每次都只有少量的编译错误。其次,编译错误是您别无选择,只能一一解决(否则,您将无法获得可执行文件),这并不像听起来那么困难。 - Shahbaz
@Shahbaz 这还不够。 - Det
5个回答

4
经过一些研究,似乎没有(简单的)方法在C++中本地测试某些编译错误。如果您可以使用C++2a,则可能不再是这种情况。因为我目前正在为模板代码编写测试,所以我还尝试测试编译时错误。特别是,我想测试一个负面特性,从而提供保证某个结构将无法编译的构造。使用c++20 requires表达式可以实现如下简单例子:在下面,我检查不存在的函数invalid_function是否不能在类型为S的结构上调用。
struct S {}; //< Example struct on which I perform the test
template <typename T> constexpr bool does_invalid_function_compile = requires(T a) {
  a.invalid_function();
};
static_assert(!does_invalid_function_compile<S>, "Error, invalid function does compile.");

请注意,您可以使用测试框架的相应函数替换静态断言,并在运行时记录测试错误,从而避免编译测试停止其他测试的执行。

问题示例

当然,可以根据问题中描述的场景进行适当调整,例如:

/// Test struct with deleted assignment operator
struct myClass {
  auto operator=(myClass const &) = delete;
};
/// Requires expression which is used in order to check if assigment is possible
template <myClass cl1, myClass cl2> constexpr bool does_assignment_compile = requires() {
  cl1 = cl2;
};


int main() {
  myClass cl1;
  myClass cl2;
  // Note that static assert can only be used if this can be known at compile time. Otherwise use
  // the boolean otherwise.
  static_assert(!does_assignment_compile<cl1, cl2>);
}

这段代码可以在Compiler Explorer上找到。

应用场景

我将其用于模板元编程,以确保代码符合某些理论约束条件。


4

try-catch语句发生在运行时,而编译器在编译时静态地尝试链接所调用的函数,因此编译将始终失败。

或者,如果您愿意使用C++异常,那么您可以实现拷贝和赋值方法,并使它们公开,在这些函数体中抛出异常。请注意,在几乎所有情况下,如果有选择,您应该优先选择静态/编译时检查而不是运行时检查。


谢谢Preet,我做了这个并且它帮助我更好地理解了异常。现在我明白编译时检查要更可取一些。 - Jared Roder

4
这些是编译器错误,而不是异常。异常是程序员抛出运行时错误并捕获/处理它们的机制。编译器无法构建可执行文件,因为它认为代码存在格式问题,是无效的C++代码。
如果您想将其作为运行时错误,请将方法设置为public /使用friend或者其他方式提供访问权限,并在该方法的定义中引发异常,然后在调用代码中捕获和处理异常。
然而,我不认为这样做有任何意义。始终优先选择编译时错误而不是运行时错误。一直如此。
C++标准定义了什么是有效或无效的代码,其中一些内容未定义,其他内容由实现编译器的人决定。任何符合标准的C++编译器都会报错,因为某些内容不符合标准/定义,因此是无效的。通常,这些错误表示某些内容模糊或根本没有意义,需要修改您所编写的内容。
运行时错误要么是崩溃,要么是从用户角度看来不期望和不想要的行为。编译器错误是编译器说“我不理解你在说什么,这没有意义”。编译器警告是编译器说“我会让你这么做,但我可能不应该这样做。你确定这就是你想要的吗?”。

3

你真正想测试的不是编译器失败,而是想测试关于你的类的某些假设。

在你的测试文件中,加入#include <type_traits>

然后添加

assert((std::is_assignable <myClass, myClass> ::value) == FALSE);
assert((std::is_copy_assignable<myClass> ::value) == FALSE);
assert((std::is_copy_constructible<myClass> ::value) == FALSE);

您可以在这里查看可以检查的各种特征: http://en.cppreference.com/w/cpp/types

请注意,大多数这些函数需要编译C++11才能使用。

(如首次在断言代码不会编译中所述)


1
更好的做法是使用 static_assert 而不是 assert,这样它会触发编译错误。 - Tom

0

这种编译错误无法被抑制。从C++标准的角度来看,它们是错误的。

当然,你可以在自己的(或经过修补的)编译器中抑制其中一些。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接