考虑声明一个不受约束的
ns::operator*
。在块作用域中的 using namespace ns
并调用函数 foo<T>
之后,clang 在读取 ns::operator*
内基于范围的循环的迭代器时使用 foo
。不涉及 ns
中的其他类型,因此 ADL 应该不会产生候选者。
在以下示例中,
static_assert
失败并显示消息:
错误:静态断言由于要求“std::is_same_v
”而失败
汇编代码显示
ns::operator*
是由clang使用的。 gcc 和 msvc 的断言都通过了!
namespace ns {
template <typename T>
constexpr auto operator*(T&& /*value*/) {
struct custom_type {};
return custom_type{};
};
} // namespace ns
template <typename T>
constexpr void foo() {
std::vector<T> vec{};
for (const auto& curr : vec) {
static_assert(std::is_same_v<const T&, decltype(curr)>);
}
}
int main() {
using namespace ns;
foo<int>();
}
这是一个 godbolt 链接:https://godbolt.org/z/z5vf48Mda。
我无法通过手动调用
operator*
内的 foo
来重现问题。此外,当 foo
未模板化 或 不是 constexpr
时,断言将通过 clang。这就好像 clang 内联 foo<T>
一样,因此将 ns::operator*
视为有效的候选者。我几乎可以肯定 clang 已损坏,因为该问题仅在使用基于范围的循环时出现。
clang 或 gcc/msvc 是否正确,或者我们正在处理未指定/未定义的行为?
Clang 似乎有一个错误。
using namespace ns
中的 main
不应该对 foo
中的查找产生任何影响,并且通过任何形式的查找都不可能在 ns::operator*
中找到 foo
。
如果未声明
foo
,Clang 不会产生此行为 constexpr
,尽管这应该不会对名称查找产生任何影响。
如果将 range-for 循环替换为等效的简单循环结构,也不会产生此行为。
这向我表明,在 range-for 循环中如何处理查找存在一个错误。