SFINAE断言代码不会编译 [英] SFINAE to assert() that code DOES NOT compile
问题描述
我确信必须使用SFINAE(可能与宏一起使用)来static_assert(),这样任意代码都不会编译。
I feel certain it must be possible to use SFINAE (possibly with Macros) to static_assert() that arbitary code will not compile.
在我的代码库中有一些复杂的情况,其中我有一个我想禁止使用临时类的类(我相信这种模式是):
There are some complex cases in my code-base, where i have a class that I want to forbid taking temporaries (I believe the pattern is):
class(const class&& tmp)=delete; /* deny copying from an unnamed temporary */
class (class&& rhs){/* allow construction from non-temporary rvalue*/}
当前,我检查了不需要的构造函数,但是没有编译,但是我必须注释掉它才能使测试再次编译!
Currently, I check an undesired constructor DOES NOT compile, but then of course I have to comment it out to get the tests to compile again!
如果可以的话:
static_assert(DOES_NOT_COMPILE(myclass_t("Hello")));
const char* help = "HELP";
static_assert(!DOES_NOT_COMPILE(myclass_t(help)));
// COMPILES() might be better here :-)
这对我有帮助很大,但是我似乎找不到通用的SFINAE解决方案。仅限于C ++ 14,因此:
That would help me a great deal, but I cannot seem to find a general SFINAE solution. C++14 only, so:
if constexpr
不可用。
推荐答案
以下宏可让您重写SFINAE -不友好的表达式,例如 [](auto&& x){return x + 1; }
以SFINAE友好的方式。
The following macro lets you rewrite a SFINAE-unfriendly expression such as [](auto&&x) { return x+1; }
in a SFINAE-friendly way.
#define RETURNS(...)\
noexcept(noexcept(__VA_ARGS__))\
->decltype(__VA_ARGS__)\
{ return __VA_ARGS__;}
因此,您可以这样重写上述lambda表达式:
So that lets you rewrite the above lambda expression like this:
[](auto&&x) RETURNS( x+1 )
,或者,另一个示例:
struct { template<class X> auto operator()(X&&x) RETURNS(x+1) };
,它对SFINAE友好。实际上并不需要 RETURNS
,但是它使大部分代码更加简洁。有一个c ++ 20 建议用SO自己的@barry将 RETURNS
替换为 =>
。
and it is SFINAE friendly. RETURNS
isn't actually required, but it makes much of the code so much cleaner. There is a c++20 proposal to replace RETURNS
with =>
by SO's own @barry.
接下来,我们需要测试是否可以调用函数对象。
Next we need to be able to test if a function object can be called.
namespace details {
template<class, class, class...>
struct can_invoke:std::false_type{};
template<class F, class...Args>
struct can_invoke<
F,
std::void_t<std::result_of_t<F&&(Args&&...)>>,
Args...
>:
std::true_type
{};
}
template<class F, class...Args>
using can_invoke=details::can_invoke<F,void,Args...>;
我们快到了。 (这是该技术的核心;我有时会使用 can_apply
接受 template< class ...> class Z
而不是这里的 F类
。) c ++ 17 具有相似的特征;
we are almost there. (This is the core of the technique; I sometimes use can_apply
that takes template<class...>class Z
instead of class F
here.) c++17 has a similar trait; it can be used instead.
test_invoke
可调用并返回可调用的测试器。可调用的测试器接受参数,并根据可以使用这些参数调用原始可调用对象返回真假类型。
test_invoke
takes callable and returns a callable tester. A callable tester takes arguments, and returns true or false types based on "could the original callable be called with these arguments".
template<class F>
constexpr auto test_invoke(F&&){
return [](auto&&...args) RETURNS( can_invoke< F, decltype(args)... >{} );
}
,我们到了。如果您愿意使用纯类型,可以跳过 test_invoke
,但是使用值可以消除一些错误。
and here we are. test_invoke
can be skipped if you are willing to work with pure types, but working with values can eliminate some bugs.
auto myclass_ctor=[](auto&&...args)RETURNS(myclass_t(decltype(args)(args)...));
myclass_ctor
是表示构造的可调用对象 myclass_t
。
myclass_ctor
is a callable object that represents constructing myclass_t
.
static_assert(!test_invoke(myclass_ctor)("Hello") );
或
template<class C>
auto ctor=[](auto&&...args)RETURNS(C(decltype(args)(args)...));
static_assert(!test_invoke(ctor<myclass_t>)("Hello") );
这需要constexpr lambda,即 c ++ 17 功能,但功能较早。没有它就可以做到,但是很难看。另外,动词ctor的要求很烦人,无法在 c ++ 14 。
this requires constexpr lambda, a c++17 feature but an early one. It can be done without it but it gets ugly. Plus move ctor requirement of elision is annoying to work around in c++14.
要翻译为 c ++ 14 ,将每个lambda替换为具有适当constexpr特殊成员函数的手动函数对象。如上所述,返回值
同样适用于 operator()
。
To translate to c++14, replace every lambda with a manual function object with appropriate constexpr special member functions. RETURNS
applies to operator()
just as well, as demonstrated above.
要绕过elevance移动ctor要求,请 RETURNS(void(blah))
。
To get around elision move ctor requrement, RETURNS(void( blah ))
.
道歉对于任何打字;我在打电话。
Apologies for any tyops; I am on phone.
这篇关于SFINAE断言代码不会编译的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!