简而言之:
如何编写测试,检查我的类是不可复制还是可复制,但只能移动并可移动分配?
一般情况下:
如何编写测试,确保特定代码不编译?像这样:
// 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有关,但是有一些现成的解决方案,可能是在提升吗?
答案 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_type
和std::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,则显示为编译器错误。 静态断言声明可能出现在块作用域中(作为块 声明)和一个类体(作为成员声明)