是否可以为不应编译的表达式表达 static_assert?
Is it possible to express a static_assert for an expression that should not compile?
我想用以下形式表达一个static_assert:
static_assert(expression should not compile);
让我添加一个完整的例子:
template <bool Big>
struct A{};
template <>
struct A<true>
{
void a() {}
};
A<false> b;
static_assert(!compile(b.a()));
or
static_assert(!compile(A<false>::a()));
因此,我们的想法是能够确保表达式(具有有效语法)不会编译。
如果可能解决方案只使用C++11会更好。
好的,考虑到您问题的上下文有些模糊,这个答案可能不适合您的情况。然而,我发现这是一个非常有趣的挑战。
显然,如评论中所述,解决方案必须利用某种(表达式)SFINAE。基本上,我们需要的是一种更通用的检测惯用语变体。但是,这里主要有两个问题:
1) 为了启动 SFINAE,我们需要某种模板。
2) 为了提供compile(XXX)
语法,我们需要在宏中创建这些模板"on the fly"。否则我们必须提前为每个测试定义一个测试函数。
第二个约束使事情变得相当困难。我们可以在 lambda 内部定义结构和函数。不幸的是,那里不允许使用模板。
这是我能够达到的程度(不是您想要的 100%,但相对接近)。
通常,表达式-SFINAE-检测器利用(模板)函数重载或class 模板特化。由于两者都不允许在 lambda 内部使用,因此我们需要一个额外的层:一个仿函数,它接受一堆 lambda 并调用最适合调用参数的那个。这通常与 std::variant
.
结合使用
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
现在我们可以像这样创建一个检测器:
auto detector = overloaded{
[](auto, auto) -> std::false_type {return {};}
,
[](auto x, int)-> decltype(decltype(x)::a(), std::true_type{}){ return {};}
};
static_assert(!detector(A<false>{}, int{}));
static_assert(detector(A<true>{}, int{}));
现在,我们可以定义一个宏,为所需的表达式定义和调用一个向量:
#define compile(obj, xpr) \
[]() { \
auto check = \
overloaded{[](auto&&, auto) -> std::false_type { return {}; }, \
[](auto&& x, int) -> decltype(x xpr, std::true_type{}) { \
return {}; \
}}; \
return decltype(check(obj, int{})){}; \
}()
这个宏创建一个lambda,将xpr
代入检测器,并对decltype(x)
进行类型推导,使SFINAE开始运行。可以按如下方式使用:
static_assert(!compile(b, .a()));
static_assert(compile(a, .a()));
int x = 0;
static_assert(compile(x, *= 5));
static_assert(!compile(x, *= "blah"));
不幸的是,它不能将类型名作为第一个参数。因此我们需要第二个宏来进行这些类型的 af 测试:
#define compile_static(clazz, xpr) \
[]() { \
auto check = overloaded{ \
[](auto, auto) -> std::false_type { return {}; }, \
[](auto x, int) -> decltype(decltype(x) xpr, std::true_type{}) { \
return {}; \
}}; \
return decltype(check(std::declval<clazz>(), int{})){}; \
}()
static_assert(!compile_static(A<false>, ::a()));
static_assert(compile_static(A<true>, ::a()));
如上所述,这不是您要求的 100%,因为我们总是需要额外的 ,
来分隔宏参数。此外,它需要两个单独的宏。也许这可以通过使用预处理器来检测 xpr
参数是否以 ::
开头来改进。当然,在某些情况下它可能不起作用。但也许这是一个起点。
它需要 c++17,但似乎适用于 gcc >= 7、clang >= 5 甚至 msvc 19。
这里是full example.
我想用以下形式表达一个static_assert:
static_assert(expression should not compile);
让我添加一个完整的例子:
template <bool Big>
struct A{};
template <>
struct A<true>
{
void a() {}
};
A<false> b;
static_assert(!compile(b.a()));
or
static_assert(!compile(A<false>::a()));
因此,我们的想法是能够确保表达式(具有有效语法)不会编译。
如果可能解决方案只使用C++11会更好。
好的,考虑到您问题的上下文有些模糊,这个答案可能不适合您的情况。然而,我发现这是一个非常有趣的挑战。
显然,如评论中所述,解决方案必须利用某种(表达式)SFINAE。基本上,我们需要的是一种更通用的检测惯用语变体。但是,这里主要有两个问题:
1) 为了启动 SFINAE,我们需要某种模板。
2) 为了提供compile(XXX)
语法,我们需要在宏中创建这些模板"on the fly"。否则我们必须提前为每个测试定义一个测试函数。
第二个约束使事情变得相当困难。我们可以在 lambda 内部定义结构和函数。不幸的是,那里不允许使用模板。
这是我能够达到的程度(不是您想要的 100%,但相对接近)。
通常,表达式-SFINAE-检测器利用(模板)函数重载或class 模板特化。由于两者都不允许在 lambda 内部使用,因此我们需要一个额外的层:一个仿函数,它接受一堆 lambda 并调用最适合调用参数的那个。这通常与 std::variant
.
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
现在我们可以像这样创建一个检测器:
auto detector = overloaded{
[](auto, auto) -> std::false_type {return {};}
,
[](auto x, int)-> decltype(decltype(x)::a(), std::true_type{}){ return {};}
};
static_assert(!detector(A<false>{}, int{}));
static_assert(detector(A<true>{}, int{}));
现在,我们可以定义一个宏,为所需的表达式定义和调用一个向量:
#define compile(obj, xpr) \
[]() { \
auto check = \
overloaded{[](auto&&, auto) -> std::false_type { return {}; }, \
[](auto&& x, int) -> decltype(x xpr, std::true_type{}) { \
return {}; \
}}; \
return decltype(check(obj, int{})){}; \
}()
这个宏创建一个lambda,将xpr
代入检测器,并对decltype(x)
进行类型推导,使SFINAE开始运行。可以按如下方式使用:
static_assert(!compile(b, .a()));
static_assert(compile(a, .a()));
int x = 0;
static_assert(compile(x, *= 5));
static_assert(!compile(x, *= "blah"));
不幸的是,它不能将类型名作为第一个参数。因此我们需要第二个宏来进行这些类型的 af 测试:
#define compile_static(clazz, xpr) \
[]() { \
auto check = overloaded{ \
[](auto, auto) -> std::false_type { return {}; }, \
[](auto x, int) -> decltype(decltype(x) xpr, std::true_type{}) { \
return {}; \
}}; \
return decltype(check(std::declval<clazz>(), int{})){}; \
}()
static_assert(!compile_static(A<false>, ::a()));
static_assert(compile_static(A<true>, ::a()));
如上所述,这不是您要求的 100%,因为我们总是需要额外的 ,
来分隔宏参数。此外,它需要两个单独的宏。也许这可以通过使用预处理器来检测 xpr
参数是否以 ::
开头来改进。当然,在某些情况下它可能不起作用。但也许这是一个起点。
它需要 c++17,但似乎适用于 gcc >= 7、clang >= 5 甚至 msvc 19。
这里是full example.