如何让编译器忽略这个计算结果为 false 的 if-constexpr?
How to make the compiler ignore this if-constexpr that evaluates to false?
我正在写一个宏,当它被用来像这样包装一个函数调用时:macro(function())
,returns a std::optional<T>
,其中 T 是 return 的值功能。到目前为止它有效,但是当函数没有 return 类型(即它 return无效)。
问题的核心在于,当函数没有 return 类型时,编译器会尝试计算 if-constexpr 的范围,该范围的计算结果为 false。
我尝试使用两个 if-constexpr 和一个与 else。我尝试内联所有内容并将其展开几行。我尝试用常量文字替换编译器计算的 constexpr 常量。到目前为止没有任何效果。
我现在拥有的宏如下所示:
#ifndef troy
#define troy(body)[](){\
constexpr auto inner_type_is_void=std::is_same<decltype(body), void>::value;\
using inner_type=std::conditional<inner_type_is_void,bool,decltype(body)>::type;\
try{\
if constexpr (inner_type_is_void) {(body); return std::optional<inner_type>{true};}\
if constexpr (!inner_type_is_void) {return std::optional<inner_type>{(body)};}\
} catch (...){\
return std::optional<inner_type>{};\
}\
}()
#endif
我是这样使用它的:
{
auto a=troy(f());
if (!a){
std::cout << "fail" << std::endl;
} else{
std::cout << "success: " << *a << std::endl;
}
}
当 f returns 是一个值时有效,但如果不是,则给出编译器错误。此编译器错误是:no matching function for call to ‘std::optional<bool>::optional(<brace-enclosed initializer list>)’
在 if-constexpr 计算为 false 的行上。
为了避免为 if constexpr
进行实例化,您需要一个实际具有实例化的上下文:您需要某种模板。在模板之外,没有 if constexpr
赋予的魔力。
一如既往,解决方案是将所有内容都包装在 lambda 中。现在,您正在使用 body
内联作为实际函数体。相反,您可以使用 lambda:
[&]() -> decltype(auto) { return body; }
然后将整个事情包装在一个立即调用的 lambda 中,该 lambda 将此 lambda 作为参数:
[](auto f){
// stuff
}([&]() -> decltype(auto) { return body; })
现在,在我标记 // stuff
的地方,我们处于模板上下文中,我们实际上可以使用 if constexpr
来避免实例化:
[](auto f){
using F = decltype(f);
using R = std::invoke_result_t<F>;
try {
if constexpr (std::is_void_v<R>>) {
f();
return std::optional<bool>(true);
} else {
return std::make_optional(f());
}
} catch (...) {
return std::optional<std::conditional_t<std::is_void_v<R>, bool, R>>();
}
}([&]() -> decltype(auto) { return body; })
现在只需添加一堆 \
就可以了。
您可以在没有宏的情况下执行此操作(我推荐):
template <class F>
constexpr auto troy(F f)
{
using R = decltype(f());
static_assert(!std::is_reference_v<R>);
if constexpr (std::is_void_v<R>)
{
try
{
f();
}
catch(...)
{
}
return std::optional<bool>{std::nullopt};
}
else
{
try
{
return std::optional<R>(f());
}
catch(...)
{
return std::optional<R>{std::nullopt};
}
}
}
auto foo1() {}
auto foo2() { return 24; }
auto test()
{
auto a = troy(foo1);
auto b = troy(foo2);
}
如果您有重载函数,有一个简单的修复方法:
auto foo(int a) -> void;
auto foo(int a, int b) -> void;
auto test()
{
// your version:
// auto f = troy(foo(1024, 11));
// my version:
auto f = troy([] { return foo(1024, 11); });
}
编译器将尽其所能查找 if 的错误分支中的错误。
由于没有依赖类型(它不是模板),因此编译器可以检查每个分支是否有错误。
所以 void 表达式的无效使用,使用未声明的符号和其他类似的东西在任何分支中都是无效的,constexpr 与否。
依赖表达式不同。如果没有类型会使分支编译,则程序只有在分支无效时才格式错误。因此,仍然不能使用未声明的符号,但肯定会有一种类型使 std::optional<inner_type>{(body)}
有效,因此编译器将不会实例化该分支。
这可以用一个函数模板来实现,然后你的宏用 lambda 调用它:
template<typename T>
auto try_impl(T closure) noexcept {
using body_type = decltype(closure());
constexpr auto inner_type_is_void = std::is_void_v<body_type>;
using inner_type = std::conditional_t<inner_type_is_void, bool, body_type>;
try {
if constexpr (std::is_void_v<body_type>) {
closure();
return std::optional<inner_type>{true};
} else {
return std::optional<inner_type>{closure()};
}
} catch(...) {
return std::optional<inner_type>{};
}
}
#ifndef troy
#define troy(body) try_impl([&]{ return body; })
#endif
我正在写一个宏,当它被用来像这样包装一个函数调用时:macro(function())
,returns a std::optional<T>
,其中 T 是 return 的值功能。到目前为止它有效,但是当函数没有 return 类型(即它 return无效)。
问题的核心在于,当函数没有 return 类型时,编译器会尝试计算 if-constexpr 的范围,该范围的计算结果为 false。
我尝试使用两个 if-constexpr 和一个与 else。我尝试内联所有内容并将其展开几行。我尝试用常量文字替换编译器计算的 constexpr 常量。到目前为止没有任何效果。
我现在拥有的宏如下所示:
#ifndef troy
#define troy(body)[](){\
constexpr auto inner_type_is_void=std::is_same<decltype(body), void>::value;\
using inner_type=std::conditional<inner_type_is_void,bool,decltype(body)>::type;\
try{\
if constexpr (inner_type_is_void) {(body); return std::optional<inner_type>{true};}\
if constexpr (!inner_type_is_void) {return std::optional<inner_type>{(body)};}\
} catch (...){\
return std::optional<inner_type>{};\
}\
}()
#endif
我是这样使用它的:
{
auto a=troy(f());
if (!a){
std::cout << "fail" << std::endl;
} else{
std::cout << "success: " << *a << std::endl;
}
}
当 f returns 是一个值时有效,但如果不是,则给出编译器错误。此编译器错误是:no matching function for call to ‘std::optional<bool>::optional(<brace-enclosed initializer list>)’
在 if-constexpr 计算为 false 的行上。
为了避免为 if constexpr
进行实例化,您需要一个实际具有实例化的上下文:您需要某种模板。在模板之外,没有 if constexpr
赋予的魔力。
一如既往,解决方案是将所有内容都包装在 lambda 中。现在,您正在使用 body
内联作为实际函数体。相反,您可以使用 lambda:
[&]() -> decltype(auto) { return body; }
然后将整个事情包装在一个立即调用的 lambda 中,该 lambda 将此 lambda 作为参数:
[](auto f){
// stuff
}([&]() -> decltype(auto) { return body; })
现在,在我标记 // stuff
的地方,我们处于模板上下文中,我们实际上可以使用 if constexpr
来避免实例化:
[](auto f){
using F = decltype(f);
using R = std::invoke_result_t<F>;
try {
if constexpr (std::is_void_v<R>>) {
f();
return std::optional<bool>(true);
} else {
return std::make_optional(f());
}
} catch (...) {
return std::optional<std::conditional_t<std::is_void_v<R>, bool, R>>();
}
}([&]() -> decltype(auto) { return body; })
现在只需添加一堆 \
就可以了。
您可以在没有宏的情况下执行此操作(我推荐):
template <class F>
constexpr auto troy(F f)
{
using R = decltype(f());
static_assert(!std::is_reference_v<R>);
if constexpr (std::is_void_v<R>)
{
try
{
f();
}
catch(...)
{
}
return std::optional<bool>{std::nullopt};
}
else
{
try
{
return std::optional<R>(f());
}
catch(...)
{
return std::optional<R>{std::nullopt};
}
}
}
auto foo1() {}
auto foo2() { return 24; }
auto test()
{
auto a = troy(foo1);
auto b = troy(foo2);
}
如果您有重载函数,有一个简单的修复方法:
auto foo(int a) -> void;
auto foo(int a, int b) -> void;
auto test()
{
// your version:
// auto f = troy(foo(1024, 11));
// my version:
auto f = troy([] { return foo(1024, 11); });
}
编译器将尽其所能查找 if 的错误分支中的错误。
由于没有依赖类型(它不是模板),因此编译器可以检查每个分支是否有错误。
所以 void 表达式的无效使用,使用未声明的符号和其他类似的东西在任何分支中都是无效的,constexpr 与否。
依赖表达式不同。如果没有类型会使分支编译,则程序只有在分支无效时才格式错误。因此,仍然不能使用未声明的符号,但肯定会有一种类型使 std::optional<inner_type>{(body)}
有效,因此编译器将不会实例化该分支。
这可以用一个函数模板来实现,然后你的宏用 lambda 调用它:
template<typename T>
auto try_impl(T closure) noexcept {
using body_type = decltype(closure());
constexpr auto inner_type_is_void = std::is_void_v<body_type>;
using inner_type = std::conditional_t<inner_type_is_void, bool, body_type>;
try {
if constexpr (std::is_void_v<body_type>) {
closure();
return std::optional<inner_type>{true};
} else {
return std::optional<inner_type>{closure()};
}
} catch(...) {
return std::optional<inner_type>{};
}
}
#ifndef troy
#define troy(body) try_impl([&]{ return body; })
#endif