c++ - GCC 不会禁用基于 SFINAE 规则的功能
问题描述
在编译以下在 Coliru 上尝试一下!,我期待 GCC 不考虑该功能
template <typename DST, typename... Ts>
std::enable_if_t<sizeof...(Ts) == 0> CheckAndSetVal(DST&) {}
用于分析,因为 sizeof 条件不满足。
namespace SFINAE
{
template <typename DST, typename... Ts>
std::enable_if_t<sizeof...(Ts) == 0> CheckAndSetVal(DST&) {}
template <typename DST, typename T1, typename T2, typename... Ts>
std::enable_if_t<!std::is_same_v<DST, T2> > CheckAndSetVal(DST& dst, T1&& cond, T2&& val, Ts&&... ts)
{
if (cond())
dst = val();
else
CheckAndSetVal(dst, std::forward<Ts>(ts)...);
}
template <typename DST, typename T1, typename T2, typename... Ts>
std::enable_if_t<std::is_same_v<DST, T2> > CheckAndSetVal(DST& dst, T1&& cond, T2&& val, Ts&&... ts)
{
if (cond())
dst = val;
else
CheckAndSetVal(dst, std::forward<Ts>(ts)...);
}
template <typename DST, typename... Ts>
void SetValue(DST& dst, Ts&&... ts)
{
CheckAndSetVal(dst, std::forward<Ts>(ts)...);
}
}
int main()
{
int i = 0;
SFINAE::SetValue(i, []() { return true; } , []() { return 222; }
, []() { return false; } , 444
);
}
但我看到 GCC 抛出以下错误,恕我直言,这与它本身是矛盾的。它抱怨它找不到与 int&、lambda 和 int 作为参数的递归函数调用之一的匹配函数。但再次说候选人应该被禁用,因为条件 sizeof...(Ts) == 0 是错误的。
candidate expects 1 argument, 3 provided
有人可以帮我理解为什么会这样吗?
main.cpp: In instantiation of 'std::enable_if_t<(! is_same_v<DST, T2>)> SFINAE::CheckAndSetVal(DST&, T1&&, T2&&, Ts&& ...) [with DST = int; T1 = main()::<lambda()>; T2 = main()::<lambda()>; Ts = {main()::<lambda()>, int}; std::enable_if_t<(! is_same_v<DST, T2>)> = void]':
main.cpp:36:19: required from 'void SFINAE::SetValue(DST&, Ts&& ...) [with DST = int; Ts = {main()::<lambda()>, main()::<lambda()>, main()::<lambda()>, int}]'
main.cpp:47:19: required from here
main.cpp:21:21: error: no matching function for call to 'CheckAndSetVal(int&, main()::<lambda()>, int)'
CheckAndSetVal(dst, std::forward<Ts>(ts)...);
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:13:40: note: candidate: 'template<class DST, class ... Ts> std::enable_if_t<(sizeof... (Ts) == 0)> SFINAE::CheckAndSetVal(DST&)'
std::enable_if_t<sizeof...(Ts) == 0> CheckAndSetVal(DST&) {}
^~~~~~~~~~~~~~
main.cpp:13:40: note: template argument deduction/substitution failed:
main.cpp:21:21: note: candidate expects 1 argument, 3 provided
CheckAndSetVal(dst, std::forward<Ts>(ts)...);
~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:16:47: note: candidate: 'template<class DST, class T1, class T2, class ... Ts> std::enable_if_t<(! is_same_v<DST, T2>)> SFINAE::CheckAndSetVal(DST&, T1&&, T2&&, Ts&& ...)'
std::enable_if_t<!std::is_same_v<DST, T2> > CheckAndSetVal(DST& dst, T1&& cond, T2&& val, Ts&&... ts)
^~~~~~~~~~~~~~
main.cpp:16:47: note: template argument deduction/substitution failed:
解决方案
首先,这个:
template <typename DST, typename... Ts>
std::enable_if_t<sizeof...(Ts) == 0> CheckAndSetVal(DST&) {}
应该只是:
template <typename DST>
void CheckAndSetVal(DST&) {}
现在,一旦我们解决了这个问题,您的第二个重载看起来像:
template <typename DST, typename T1, typename T2, typename... Ts>
std::enable_if_t<!std::is_same_v<DST, T2> >
CheckAndSetVal(DST& dst, T1&& cond, T2&& val, Ts&&... ts)
{
if (cond())
dst = val();
else
CheckAndSetVal(dst, std::forward<Ts>(ts)...); // (*)
}
在某些情况下,标记的行想要调用第三个重载(例如在您的示例程序中)。但是第三个重载实际上还没有在范围内,ADL 也找不到它。标记行的唯一候选者是这个重载本身(不是候选者,因为它是 SFINAE-d 出来的)和第一个重载(不是候选者,因为它没有接受足够的参数)。
所以你必须:
- 在第 2 次之前声明(但不定义)第 3 次重载,以便它在第 2 次的范围内。
- 添加一个虚拟的第一个参数,它是命名空间中的某个空类,
SFINAE
以便 ADL 可以让您找到稍后声明的函数 - 使所有这些成员
operator()s
成为一个类,以便您可以看到稍后声明的函数,因为类主体是完整的类上下文。然后创建CheckAndSetVal
该类类型的函数对象,而不是多个重载函数。
推荐阅读
- javascript - 使用 JavaScript 插入 HTML 元素
- excel - 更改excel中的日期格式
- c++ - 根据 a 或正则表达式中的匹配确定替换字符串
- javascript - 将对象用作 formState 时,角度表单 addControl 不起作用
- oracle - 更新 oracle 12 c 中归档的 NCHAR(2 CHAR) 时会自动添加空格/空格
- php - 在数组PHP中重复字符串x次数
- probability - 每支球队赢得淘汰赛的概率
- python - 有没有办法让函数等待图像显示,然后使用 PyAutoGui (Python) 在显示时单击图像
- php - 504 错误:无法满足请求
- python - How to combine 2 columns in pandas DataFrame?