This is a follow-up of this question: `requires` expressions and function namespaces
Trying to design an answer I stumbled on this strange behavior:
#include <cstdio>
// dummy default declaration
template <typename T>
void foo(T) = delete;
template <typename T>
void bar(T t) {
foo(t);
};
// client types
struct S1 {};
// "specializations" for S1
void foo(S1) { std::puts("foo(S1)"); }
int main() {
foo(S1{}); // calls the expected version
bar(S1{}); // try to call the deleted version the wrong version
}
ouput:
foo(S1)
foo(S1)
But if foo is inside a namespace:
// dummy default declaration
namespace N {
template <typename T>
void foo(T) = delete;
}
template <typename T>
void bar(T t) {
N::foo(t);
};
// client types
struct S1 {};
// "specializations" for S1
namespace N {
void foo(S1) { std::puts("foo(S1)"); }
} // namespace N
int main() {
N::foo(S1{}); // calls the expected version
bar(S1{}); // try to call the deleted version the wrong version
}
The call to bar does not compile as only the first occurrences of foo (the deleted templates here) are considered.
The candidate void N::foo(S1) is not considered.
Yet If, instead of function definitions, I play with template specialization, the behavior is consistent:
// a kind of trait, false by default
template<typename T> struct Trait : public std::false_type
{};
template <typename T>
constexpr bool b_struct = Trait<T>::value;
// client types
struct S1 {};
struct S2 {};
// specializations for S1
template<> struct Trait<S1> : public std::true_type
{};
int main() {
std::cout<<std::boolalpha<<b_struct<S1><<" expecting true\n";
std::cout<<std::boolalpha<<b_struct<S2><<" expecting false\n";
}
and
// a kind of trait, false by default
namespace N {
template <typename T>
struct Trait : public std::false_type {};
} // namespace N
template <typename T>
constexpr bool b_struct = N::Trait<T>::value;
// client types
struct S1 {};
struct S2 {};
// specializations for S1
namespace N {
template <>
struct Trait<S1> : public std::true_type {};
} // namespace N
int main() {
std::cout << std::boolalpha << b_struct<S1> << " expecting true\n";
std::cout << std::boolalpha << b_struct<S2> << " expecting false\n";
}
both output:
true expecting true
false expecting false
I try to understand through https://en.cppreference.com/w/cpp/language/qualified_lookup and https://en.cppreference.com/w/cpp/language/overload_resolution but to no avail.
Is this behavior normal and why?
N::foosuppresses ADL. So the name lookup forN::foois only performed at the point of declaration ofb_req, but not at the point of instantiation. At the point of declaration,foo(S1)is not visible yet. It's not about the namespace - in your first example, you'd get the same effect if you write::foo(obj)inb_req. It's all about the differences between qualified and unqualified name lookup.S1andfoo(S1)are in the same namespace. Or else, if all overloads are visible from whereb_reqis defined.namespace N { struct S3{}; void foo(S3) { std::puts("foo(S3)"); } }. Demo