禁用具有"if constexpr"和 SFINAE 的分支
disable branch with "if constexpr" and SFINAE
我想根据是否可以使用某些参数调用函数在编译时间启用/禁用分支。在if constexpr
条件下必须做什么?我可以通过std::result_of(decltype(add)(A, B))
获得结果类型,但是如何检查结果类型是否有效?(即如何将此信息转换为bool
?(
const auto add = [](const auto a, const auto b) { return a + b; };
const auto subtract = [](const auto a, const auto b) { return a - b; };
template <typename A, typename B>
void foo(A a, B b) {
if constexpr ( /* can add(a, b) be called? */ ) {
std::cout << "result of add: " << add(a, b) << std::endl;
}
if constexpr ( /* can subtract(a, b) be called? */ ) {
std::cout << "result of subtract: " << subtract(a, b) << std::endl;
}
}
首先,您需要使您的lambdas sfinae友好。
#define RETURNS(...)
noexcept(noexcept(__VA_ARGS__))
->decltype(__VA_ARGS__)
{ return __VA_ARGS__; }
const auto add = [](const auto a, const auto b) RETURNS( a + b );
const auto subtract = [](const auto a, const auto b) RETURNS( a - b );
现在可以在sfinae上下文中测试添加和亚曲线。
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_t = details::can_invoke<F, Args...>;
template<class F, class...Args>
constexpr can_invoke_t< F, Args... >
can_invoke( F&&, Args&&... ){ return {}; }
我们准备就绪:
template <typename A, typename B>
void foo(A a, B b) {
if constexpr ( can_invoke( add, a, b ) ) {
std::cout << "result of add: " << add(a, b) << std::endl;
}
if constexpr ( can_invoke( subtract, a, b ) {
std::cout << "result of subtract: " << subtract(a, b) << std::endl;
}
}
这是C 14;在C 11中,它更加尴尬,在C 17中更优雅,因为它们已经具有可以调用类型的特征(它处理了更多的角案例;但是,它也希望您使用std::invoke
调用add
(。bk_hr>
在C 17中,我有点像这样的技巧:
template<class F>
constexpr auto invoke_test( F&& ) {
return [](auto&&...args) ->
can_invoke_t<F, decltype(args)...>
{ return {}; };
}
template <typename A, typename B>
void foo(A a, B b) {
if constexpr ( invoke_test( add )( a, b ) ) {
std::cout << "result of add: " << add(a, b) << std::endl;
}
if constexpr ( invoke_test( subtract )( a, b ) {
std::cout << "result of subtract: " << subtract(a, b) << std::endl;
}
}
invoke_test
占用可可的地方,然后返回一个可调用的工作,其唯一的工作是回答"与我传递的args一起调用原始可召唤"。
您可以将sfinae放入返回类型,并让函数超载告诉呼叫是否可以进行。辅助功能can_be_called
可以如下实现:
#include <type_traits>
template<class Func, class... Args>
constexpr auto
can_be_called(Func&& func, Args&&... args)
-> decltype(
(std::forward<Func>(func)(std::forward<Args>(args)...)
, bool{}))
{ return true; }
struct Dummy {
template<class T> constexpr Dummy(T&&) {}
};
template<class... Args>
constexpr bool
can_be_called(Dummy, Args&&...) { return false; }
// test
#include <iostream>
void foo(int, int) {}
struct A{};
int main() {
if constexpr( can_be_called(foo, 1, 2) ) {
std::cout << "OKn";
}
if constexpr ( !can_be_called(foo, A{}, 2) ) {
std::cout << "NOn";
}
}
相关文章:
- IPC使用多个管道和分支进程来运行Python程序
- 为什么使用SFINAE而不是函数重载
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 如何删除peer if else分支中的冗长句子
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 如何正确地将分支添加到已存在的树中
- 提供与TMP和SFINAE的通用接口
- 如何将分支添加到已存在的TTree:ROOT
- "Inverse SFINAE"避免模棱两可的过载
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 如何删除 LLVM 中的不规则分支?
- 如何在儿童类中使用SFINAE
- 使用 SFINAE 作为模板参数的编译时递归
- 使用 SFINAE 设计模板方法
- 与SFINAE支票交朋友
- C++许多 SFINAE 风格的过载
- 禁用具有"if constexpr"和 SFINAE 的分支