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