断言代码不编译

时间:2014-05-08 16:46:43

标签: c++ c++11 boost compiler-errors sfinae

简而言之:

如何编写测试,检查我的类是不可复制还是可复制,但只能移动并可移动分配?

一般情况下:

如何编写测试,确保特定代码编译?像这样:

// 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;
}

我想与SFINAE有关,但是有一些现成的解决方案,可能是在提升吗?

6 个答案:

答案 0 :(得分:16)

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(

将代码放在这里。请注意,它必须早期失败&#34;,即在功能的签名中,而不是在身体中

  )>
>:std::true_type {};

如果&#34;在此处放置代码,则上面生成测试&#34;可以评估。

确定&#34;是否在此处放置代码&#34;无法评估,否定测试结果。

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

将是真的iff&#34;将代码放在这里&#34;在替换阶段失败。 (或者您可以通过交换上面的std::true_typestd::false_type来手动执行此操作。

在替换阶段失败不同于一般失败,因为它必须是一个表达式,你在某些方面受限制。但是,要测试是否可以复制,您可以执行以下操作:

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 {};

并移动:

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 {};

并且只移动:

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

上述一般技术依赖于SFINAE。基本特征类看起来像:

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

在这里,我们采用类型T和第二个(匿名)参数,我们默认为void。在工业强度库中,我们将其隐藏为实施细节(公共特征将转发到这种私人特征。

然后我们专注。

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

诀窍在于,当且仅当我们希望我们的测试通过时,我们才会/*some type expression*/评估类型void。如果失败,我们可以评估为非void类型,或者只是发生替换失败。

当且仅当评估为void时,我们才会得到true_type

sink_t<某种类型的表达式>技术采用任何类型表达式并将其转换为void:基本上它是替换失败的测试。图论中的sink指的是事物流入的地方,没有任何东西出现 - 在这种情况下,void什么都不是,类型会流入其中。

对于类型表达式,我们使用decltype(一些非类型表达式),这使我们可以在&#34;假&#34;中评估它。我们抛弃结果的背景。现在,为了SFINAE,非类型表达式仅被 评估。

请注意,MSVC 2013对此特定步骤的支持有限或不支持。他们称之为“表达SFINAE&#34;”。必须使用替代技术。

非类型表达式计算其类型。它实际上并没有运行,并且它不会导致ODR使用任何东西。所以我们可以使用std::declval<X>()生成&#34;假&#34;类型为X的实例。我们对左值使用X&,为左值使用X,为X const&左值使用const

答案 1 :(得分:9)

您正在寻找<type_traits>中定义的type traits来测试类型是否具有某些属性。

答案 2 :(得分:3)

如果目标是确保代码不会编译,那么你就无法做到 将它作为测试程序的一部分,否则,测试 程序不会编译。你必须在它上面调用编译器, 并查看返回代码是什么。

答案 3 :(得分:3)

AndrzejKrzemieński在一篇伟大的文章“Diagnosable validity”结尾处给出了一个很好的答案:

  

检查给定构造是否无法编译的实用方法是从C ++外部执行:使用错误构造准备一个小型测试程序,编译它,并测试编译器是否报告编译失败。这就是“负面”单元测试如何与Boost.Build一起使用。有关示例,请参阅此负面测试表单Boost.Optional library:optional_test_fail_convert_from_null.cpp。在配置文件中,它被注释为编译失败,这意味着仅在编译失败时才通过测试。

答案 4 :(得分:1)

例如,此std::is_nothrow_move_assignable<std::string>::value在编译时返回true

有关更多检查器的信息,请参见https://en.cppreference.com/w/cpp/types#Supported_operations

我建议与static_assert一起使用,请参阅https://en.cppreference.com/w/cpp/language/static_assert

现在开始 我试图检查是否可以在某个对象上调用特定方法。归结为“如果代码可以编译则断言”,并且有一种简洁而简洁的方法来检查它。

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");

如果失败,则出现上述字符串的编译错误

答案 5 :(得分:0)

您可能需要以不同的方式构建代码才能使用它,但听起来您可能正在寻找

static_assert(bool_constexpr,message)

  

Performs compile-time assertion checking   (自C ++ 11起):解释:bool_constexpr - 一个常量表达式   在语境上可以转换为布尔; message - 将使用的字符串文字   如果bool_constexpr为false,则显示为编译器错误。   静态断言声明可能出现在块作用域中(作为块   声明)和一个类体(作为成员声明)