空隙和非空隙方法之间的Sfinae调度
SFINAE dispatch between void and non-void method
我有以下内容:
template <typename T>
struct Base {
auto func() {
// do stuff
auto x = static_cast<T&>(*this).func_impl();
// do stuff
return x;
}
};
struct A : Base<A> {
int func_impl() {
return 0;
}
};
struct B : Base<B> {
void func_impl() {
}
};
int main() {
A a;
int i = a.func();
B b;
b.func();
return 0;
}
问题在于,我无法在派生类中声明func_impl
的返回类型为void
,如B
所示。我试图使用这样的sfinae解决问题:
template <typename T>
struct Base {
template <typename = enable_if_t<!is_void<decltype(declval<T>().func_impl())>::value>>
auto func() {
// do stuff
auto x = static_cast<T&>(*this).func_impl();
// do stuff
return x;
}
template <typename = enable_if_t<is_void<decltype(declval<T>().func_impl())>::value>>
void func() {
// do stuff
static_cast<T&>(*this).func_impl();
// do stuff
}
};
但是编译器给出了错误:invalid use of incomplete type 'struct A'
和invalid use of incomplete type 'struct B'
。有没有办法完成我想要的东西?
尝试
template <typename T>
struct Base {
template <typename U = T, typename = enable_if_t<!is_void<decltype(declval<U>().func_impl())>::value>>
auto func() {
// do stuff
return static_cast<T&>(*this).func_impl();
}
template <typename U = T, typename = enable_if_t<is_void<decltype(declval<U>().func_impl())>::value>>
void func() {
// do stuff
static_cast<T&>(*this).func_impl();
}
};
我的意思是... Sfinae在模板上应用;如果要在类中启用/禁用方法,则必须是模板方法(类/struct是模板类/结构的事实不计算:方法是否必须为模板。
和sfinae部分(在这种情况下为std::enable_if_t
)必须取决于该方法的模板(在我的示例中,U
)。
P.S:无论如何,我看不到返回void
这样的情况:
auto x = static_cast<T&>(*this).func_impl();
// do stuff
return x;
呼叫常规空隙类型。换句话说,由于您不需要在这里需要 x
,所以您只需要返回它,您真的需要需要 func()
返回 void
吗?我发现通常不是这种情况。人们不应该使用的任何旧的空类型都足够好。因此,让我们可以轻松地不复制而撰写此情况:
namespace details {
struct Void { }; // not intended to be used anywhere
}
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
std::enable_if_t<!std::is_void<R>::value, int> = 0>
R invoke_void(F&& f, Args&&... args) {
return std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
template <typename F, typename... Args,
typename R = std::invoke_result_t<F, Args...>,
std::enable_if_t<std::is_void<R>::value, int> = 0>
details::Void invoke_void(F&& f, Args&&... args) {
std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
return details::Void{};
}
此实现使用C 17库功能,但可以在C 14中实现。这为我们提供了一个invoke()
,可以将void
换成Void
,这使您只能写入:
auto func() {
// do stuff
auto x = invoke_void([](auto& x){ return x.func_impl(); },
static_cast<T&>(*this));
// do stuff
return x;
}
这有点言语,但至少我们不必复制func()
-只有一个功能可以处理两种情况。
另一种替代方案,根据您的解释更简单或更复杂,是重新排序func()
的主体:
auto func() {
// do stuff
scope_exit{
// do stuff after func_impl is invoked
};
return static_cast<T&>(*this).func_impl();
}
这使您可以正确订购操作,甚至不需要常规空隙。但是,func_impl
后逻辑被放置在其之前 - 可能会令人困惑。但是好处是此功能仍然可以返回void
。
So上有许多scope_exit
之类的东西的实现。
- 如何在c++中实现处理器调度模拟器
- 为什么使用SFINAE而不是函数重载
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 提供与TMP和SFINAE的通用接口
- "Inverse SFINAE"避免模棱两可的过载
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 如何在 C++17 STL 并行算法中处理调度?
- 如何在儿童类中使用SFINAE
- 使用 SFINAE 作为模板参数的编译时递归
- 无法使用迭代器标记调度实例化模板
- 使用 SFINAE 设计模板方法
- 与SFINAE支票交朋友
- C++许多 SFINAE 风格的过载
- 是否可以混合使用SFINAE和模板专业化?
- SFINAE和标签调度之间的差异
- 以不同的方式调度 r 值和 l 值,并使用 sfinae 禁用一个选项
- 空隙和非空隙方法之间的Sfinae调度