C++ 断言代码不可编译

C++ 断言代码不可编译,c++,c++11,boost,compiler-errors,sfinae,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:

简而言之:

如何编写一个测试,检查我的类是否不可复制或复制可分配,而只可移动和移动可分配

一般来说:

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

// 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;
}
//可移动但不可复制的类
结构A
{
A(常数A&)=删除;
A(A&&{}
};
void DoCopy()
{
A a1;
A a2=a1;
}
void DoMove()
{
A a1;
a2=std::move(a1);
}
void main()
{
//如何定义这些检查?

if(COMPILES(DoMove))std::cout是您正在寻找的,在
中定义,用于测试类型是否具有某些属性。

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

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(
如果可以评估“将代码放在此处”,则上面生成一个测试

要确定是否无法计算“在此处放置代码”,请否定测试结果

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

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 {};
诀窍在于,当且仅当我们希望测试通过时,我们使
/*某些类型表达式*/
计算为类型
void
。如果测试失败,我们可以计算为非
void
类型,或者简单地让替换失败发生

当且仅当其计算结果为
void
时,我们才能得到
true\u type

sink\u t
技术接受任何类型的表达式,并将其转换为
void
:基本上,它是一种替代失败的测试。
sink
在图论中指的是一个事物流入而没有任何东西流出的地方——在这种情况下,
void
什么都不是,一种测试d类型流入其中

对于类型表达式,我们使用了
decltype(
一些非类型表达式
,这让我们可以在一个“假”上下文中对其求值,在这个上下文中我们只会丢弃结果。现在,非类型表达式的求值仅用于SFINAE

请注意,MSVC 2013对此特定步骤的支持有限或没有。他们称之为“expression SFINAE”。必须使用其他技术


非类型表达式计算其类型。它实际上没有运行,也不会导致ODR使用任何东西。因此,我们可以使用
std::declval()
生成“false”类型
X
的实例。我们使用
X&
表示左值,
X
表示右值,
X常量和
const
表示左值。

要使用它,您的代码结构可能有点不同,但听起来您可能需要

静态断言(bool\u constexpr,消息)

(从C++11开始):解释:
bool_constexpr
-一个常量表达式 可在上下文中转换为bool;
消息
-将 如果bool_constexpr为false,则显示为编译器错误。 静态断言声明可能出现在块范围内(作为块) 声明)和类主体内部(作为成员声明)


安德烈·克泽米斯基(Andrzej Krzemieński)的一篇伟大的文章“最后给出了一个很好的答案:

检查给定构造是否编译失败的一种实用方法是从C++外部执行:准备一个带有错误构造的小测试程序,编译它,并测试编译器是否报告编译失败。这就是“否定”的方式单元测试与Boost.Build一起工作。例如,请参阅此负面测试表单Boost。可选库:可选的\u test\u fail\u convert\u from_null.cpp。在配置文件中,它被注释为compile fail,这意味着只有在编译失败时测试才会通过


例如,这个
std::is\u nothrow\u move\u assignable::value
在编译时返回
true

有关更多跳棋,请参阅

我建议将其与
static\u assert
一起使用,请参见

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

使用canCallPrintYesOn=decltype(::std::declval().printYes())的模板; constexpr bool canCallPrintYesOn\u MyType=std::experimental::is\u detected::value; static_assert(canCallPrintYesOn_MyType,“应该能够在此对象上调用printYes(void));

如果失败,您将得到上面字符串的编译错误

如果(不编译(DoCopy))std::cout related:如何对蓄意编译错误进行单元测试?在@user1810087,这就是他要问的问题…太好了!那么一般问题呢?即如何检查任意代码?您不应该有未编译的代码。我假设您真正想要的是测试类型的属性,而不是实际测试如果代码未编译,则不会。如果代码未编译,您的编译器肯定会告诉您,对吗?嗯,目前是的,您的答案对我来说已经足够了。我只是好奇。我相信这是不可能的,因为设计上是不可能的。如果您的代码未编译,则您无法运行它。因此,您的检查将永远不会执行。但好消息是,如果代码未编译编译,你的编译器会告诉你为什么,所以基本上编译器已经在为你做检查了。或者类似的事情。编译器是唯一知道是否可以编译某些代码块的实体。(例如,一个不完全符合标准的编译器将无法编译出完美的标准代码——这是唯一可能知道这一点的东西。)因此,与其问是否有某种方法可以检查某个东西是否编译,不如看看是否可以将问题转化为更可行的问题。例如:使用
std::is_move\u assignable::value&&std::is_move\u constructible::value
为什么不?失败是这里的一个选项;事实上,它是唯一的选项。如果程序
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 >
{};
template<class T, typename=void> struct whatever:std::false_type{};
template<typename T>struct whatever<T, /*some type expression*/>:std::true_type{};