简体   繁体   English

断言代码不能编译

[英]Assert that code does NOT compile

In short:简而言之:

How to write a test, that checks that my class is not copyable or copy-assignable, but is only moveable and move-assignable?如何编写测试,以检查我的类是否不可复制或可复制分配,而只能移动和可移动分配?

In general:一般来说:

How to write a test, that makes sure that a specific code does not compile?如何编写测试以确保特定代码不会编译? Like this:像这样:

// Movable, but non-copyable class
struct A
{
  A(const A&) = delete;
  A(A&&) {}
};

void DoCopy()
{
  A a1;
  A a2 = a1;
}

void DoMove()
{
  A a1;
  A a2 = std::move(a1);
}

void main()
{
  // How to define these checks?
  if (COMPILES(DoMove)) std::cout << "Passed" << std::endl;
  if (DOES_NOT_COMPILE(DoCopy)) std::cout << "Passed" << std::endl;
}

I guess something to do with SFINAE, but are there some ready solutions, maybe in boost?我猜想与 SFINAE 有关系,但是否有一些现成的解决方案,也许是在提升?

template<class T>struct sink{typedef void type;};
template<class T>using sink_t=typename sink<T>::type;

template<typename T, typename=void>struct my_test:std::false_type{};
template<typename T>struct my_test<T,
  sink_t<decltype(

put code here.把代码放在这里。 Note that it must "fail early", ie in the signature of a function, not in the body请注意,它必须“尽早失败”,即在函数的签名中,而不是在函数体中

  )>
>:std::true_type {};

The above generates a test if the "put code here" can be evaluated.如果可以评估“将代码放在这里”,上面会生成一个测试。

To determine if "put code here" cannot be evaluated, negate the result of the test.要确定是否无法评估“将代码放在这里”,请否定测试结果。

template<class T>using not_t=std::integral_constant<bool, !T::value>;
not_t< my_test< int > >::value

will be true iff "put code here" fails at the substitution stage.如果“将代码放在这里”在替换阶段失败,则为真。 (or you can do it more manually, by swapping std::true_type and std::false_type above). (或者您可以通过交换上面的std::true_typestd::false_type手动完成更多操作)。

Failing at the substitution stage is different than general failure, and as it has to be an expression you are somewhat limited in what you can do.替换阶段的失败与一般的失败不同,因为它必须是一种表达方式,所以你能做的事情有些有限。 However, to test if copy is possible, you can do:但是,要测试是否可以复制,您可以执行以下操作:

template<typename T, typename=void>struct copy_allowed:std::false_type{};
template<typename T>struct copy_allowed<T,
  sink_t<decltype(
    T( std::declval<T const&>() )
  )>
>:std::false_type {};

and move:并移动:

template<typename T, typename=void>struct move_allowed:std::false_type{};
template<typename T>struct move_allowed<T,
  sink_t<decltype(
    T( std::declval<T>() )
  )>
>:std::false_type {};

and only move:并且只移动:

template<typename T>struct only_move_allowed:
  std::integral_constant<bool, move_allowed<T>::value && !copy_allowed<T>::value >
{};

The general technique above relies on SFINAE.上面的一般技术依赖于 SFINAE。 The base traits class looks like:基本特征类如下所示:

template<class T, typename=void> struct whatever:std::false_type{};

Here, we take a type T , and a second (anonymous) parameter we default to void .在这里,我们采用类型T和第二个(匿名)参数,我们默认为void In an industrial strength library, we'd hide this as an implementation detail (the public trait would forward to this kind of private trait.在工业强度库中,我们会将其隐藏为实现细节(公共 trait 将转发到这种私有 trait。

Then we specialize.然后我们专攻。

template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{};

the trick is that we make /*some type expression*/ evaluate to the type void if and only if we want our test to pass.诀窍是当且仅当我们希望我们的测试通过时,我们使/*some type expression*/评估为类型void If it fails, we can either evaluate to a non- void type, or simply have substitution failure occur.如果失败,我们可以评估为非void类型,或者只是发生替换失败。

If and only if it evaluates to void do we get true_type .当且仅当它评估为void ,我们true_type得到true_type

The sink_t< some type expression > technique takes any type expression and turns it into void : basically it is a test for substitution failure. sink_t< some type expression >技术采用任何类型表达式并将其转换为void :基本上它是对替换失败的测试。 sink in graph theory refers to a place where things flow into, and nothing comes out of -- in this case, void is nothing, and the type flows into it.图论中的sink是指事物流入的地方,什么也没有流出——在这种情况下, void是虚无,类型流入其中。

For the type expression, we use decltype( some non-type expression ) , which lets us evaluate it in a "fake" context where we just throw away the result.对于类型表达式,我们使用decltype( some non-type expression ) ,这让我们可以在“假”上下文中对其进行评估,而我们只是丢弃结果。 The non-type expression is now being evaluated only for the purpose of SFINAE.现在出于 SFINAE 的目的评估非类型表达式。

Note that MSVC 2013 has limited or no support for this particular step.请注意,MSVC 2013 对此特定步骤的支持有限或不支持。 They call it "expression SFINAE".他们称之为“表达 SFINAE”。 Alternative techniques have to be used.必须使用替代技术。

The non-type expression gets its type evaluated.非类型表达式得到它的类型求值。 It isn't actually run, and it does not cause ODR usage of anything.它实际上并未运行,并且不会导致任何 ODR 使用。 So we can use std::declval<X>() to generate "fake" instances of a type X .所以我们可以使用std::declval<X>()来生成类型X “假”实例。 We use X& for lvalues, X for rvalues, and X const& for const lvalues.我们使用X&用于左值, X为右值,和X const&const左值。

您正在寻找在<type_traits>定义的类型特征,以测试类型是否具有某些属性。

If the goal is to ensure that the code won't compile, you can't have it as part of your test program, since otherwise, your test program won't compile.如果目标是确保代码无法编译,则不能将其作为测试程序的一部分,否则,您的测试程序将无法编译。 You have to invoke the compiler on it, and see what the return code is.您必须在其上调用编译器,并查看返回码是什么。

A good answer is given at the end of a great article "Diagnosable validity " by Andrzej Krzemieński: Andrzej Krzemieński 在一篇很棒的文章“可诊断有效性”的结尾给出了一个很好的答案:

A practical way to check if a given construct fails to compile is to do it from outside C++: prepare a small test program with erroneous construct, compile it, and test if compiler reports compilation failure.检查给定构造是否无法编译的一种实用方法是从 C++ 外部进行:准备一个带有错误构造的小型测试程序,编译它,然后测试编译器是否报告编译失败。 This is how “negative” unit tests work with Boost.Build.这就是“负面”单元测试如何与 Boost.Build 一起工作。 For an example, see this negative test form Boost.Optional library: optional_test_fail_convert_from_null.cpp.有关示例,请参阅此负面测试表单 Boost.Optional 库:optional_test_fail_convert_from_null.cpp。 In configuration file it is annotated as compile-fail, meaning that test passes only if compilation fails.在配置文件中,它被注释为 compile-fail,这意味着只有在编译失败时测试才通过。

for example this std::is_nothrow_move_assignable<std::string>::value returns true in compile-time.例如这个std::is_nothrow_move_assignable<std::string>::value在编译时返回true

for more checkers see https://en.cppreference.com/w/cpp/types#Supported_operations有关更多检查器,请参阅https://en.cppreference.com/w/cpp/types#Supported_operations

i recommend using it along with static_assert , see https://en.cppreference.com/w/cpp/language/static_assert我建议将它与static_assert一起使用,请参阅https://en.cppreference.com/w/cpp/language/static_assert

now in general i was trying to check if i can call a specific method on some object.现在一般我试图检查我是否可以在某个对象上调用特定方法。 this boils down to "assert if this code compiles" and there is a neat and short way to check it.这归结为“断言此代码是否编译”,并且有一种简洁而简短的方法来检查它。

template<typename T> using canCallPrintYesOn = decltype(::std::declval<T>().printYes());

constexpr bool canCallPrintYesOn_MyType = std::experimental::is_detected<canCallPrintYesOn, MyType>::value;

static_assert(canCallPrintYesOn_MyType, "Should be able to call printYes(void) on this object");

if this fails, you get compile error with above string如果失败,您将收到上述字符串的编译错误

You might have to structure your code a bit differently to use it, but it sounds like you might be looking for您可能需要以稍微不同的方式构建代码才能使用它,但听起来您可能正在寻找

static_assert ( bool_constexpr , message )静态断言( bool_constexpr ,消息)

Performs compile-time assertion checking (since C++11): Explanation: bool_constexpr - a constant expression that is contextually convertible to bool;执行编译时断言检查(C++11 起): 说明: bool_constexpr - 一个在上下文中可转换为 bool 的常量表达式; message - string literal that will appear as compiler error if bool_constexpr is false. message - 如果 bool_constexpr 为 false,则将显示为编译器错误的字符串文字。 A static assert declaration may appear at block scope (as a block declaration) and inside a class body (as a member declaration)静态断言声明可能出现在块作用域(作为块声明)和类体内(作为成员声明)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM