这是我的示例代码:
#include <type_traits>
template <bool B>
struct S {
// #1
// template <bool C = B, class = std::enable_if_t<!C>>
// operator S<true>() const { return {}; }
// #2
// operator S<true>() const { return {}; }
};
int main() {
S<true> b1;
S<false> b2;
}
当使用 g++ ( https://godbolt.org/z/P46qE8EGG ) 编译时,无论启用选项 #1、选项 #2 还是两者都启用,我都不会收到警告。当我启用 #2 时,我希望收到一个警告,指出它是一个自转换函数,但也许 g++ 没有这样的警告。
真正的问题是用 clang 编译时。仅当我启用 #1 时,我才会收到以下警告:
错误:永远不会使用将“S”转换为其自身的转换函数[-Werror,-Wclass-conversion]
启用 #2 不会触发此警告,禁用b1
或的实例化也b2
不会阻止它。到底是怎么回事?这是 clang 的错误吗?
编辑: 我具体指的是为什么#1 产生警告。其他信息旨在展示我试图将问题缩小到更小的工作示例的尝试
正确的,人们可以希望得到编译器警告(非标准最佳实践),但不能保证某个编译器会提供它们。
Clang 不警告 是正确的
#2
,因为它可能会被调用,在删除定义的示例中更容易显示:为什么?由于隐式复制构造函数复制
然而,
S<false>
intoS<true>
并不是相同意义上的复制操作。当“copy from”类为 时,模板构造函数也会出现
S<false>
,因此乍一看,Clang 似乎错误地警告该函数永远不会被使用:然而,Clang 可能不够聪明,无法分析 SFINAE,我们可以看到 SFINAE 构造对于何时或何时不发出警告来说是一个转移注意力的话题,因为我们在以下情况下得到相同的诊断:
我们意识到,当我们实例化 时,Clang 会发出警告
S<true>
,对于这种情况,它实际上是正确的:该函数永远不会被调用。此时我们可能想知道为什么它不发出警告
#1
。模板函数 ( ) 在重载决策中的排名可能会#1
低一级,或者非依赖非模板函数 (#2
) 的分析仅发生一次,与包含类模板特化的实例化无关。如果我们确保该函数在任何实例化中都不会转换为 self,则警告将按预期消失:
如果 Clang 拒绝针对此问题的错误报告,我不会感到惊讶,因为警告对于其标记的专业化是准确的,除非做出一些英勇的努力来理解何时何地某个模板函数不是 SFINAE:d或不。