GCC不会禁用基于SFINAE规则的函数
GCC does not disable a function based on SFINAE rules
编译以下内容时,请在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抛出了以下错误,IMHO本身就是一个矛盾。它抱怨说,它找不到一个匹配的函数来调用带有int&,lambda和int作为参数。但再次表示,候选人是应该被禁用的,因为条件、大小。。。(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)...); // (*)
}
在某些情况下,标记的行想要调用3rd重载(例如在示例程序中)。但第三次过载实际上还不在范围内,ADL也找不到它。标记线的唯一候选者是这个重载本身(它不是候选者,因为它是SFINAE-d out)和第一个重载(它不是候选,因为它没有足够的参数)。
所以你必须:
- 在第二个重载之前声明(但不要定义)第三个重载,以便它在第二次重载的范围内
- 在名称空间
SFINAE
中添加一个伪的1st参数,该参数是某个空类,这样ADL就可以查找稍后声明的函数 - 使所有这些成员成为类的
operator()s
,这样您就可以看到稍后声明的函数,因为类体是完整的类上下文。然后使CheckAndSetVal
成为该类类型的函数对象,而不是多个重载函数
相关文章:
- 此代码是否违反一个定义规则
- 为什么使用SFINAE而不是函数重载
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 生成文件不对文件使用隐式规则
- 使用在用于SFINAE的void_t中具有参数的方法
- 变量可能尚未初始化[MIRA 2012规则9.1,强制性]
- 静态结构和一个定义规则
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 尽管遵循了规则,内存泄漏在哪里
- 提供与TMP和SFINAE的通用接口
- "Inverse SFINAE"避免模棱两可的过载
- 这是关于成员访问规则的正确摘要吗
- uint_not_usable_without_attribute在业力规则中使用数字生成器时静态断言失败
- 增强精神解析器规则以检测语句中的特殊结尾
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 有关不完整类型的Sfinae的特殊规则
- GCC不会禁用基于SFINAE规则的函数
- SFINAE过载,必须考虑这些规则
- 如何编写SFINAE来测试语法分析器规则