I'm trying to use a concept that applies to both built-in and user-defined types. In my setting, it is inconvenient to forward-declare certain functions (because the functions depend indirectly on the concepts causing my problem).
My problem is that concepts don't recognize functions that were not previously declared if those functions have arguments of built-in type. Here's a minimal non-working example:
struct S {};
// void func(bool); // Works if this line uncommented
template<typename T> concept has_func =
requires(T t) { func(t); };
void func(S) {}
static_assert(has_func<S>); // OK
void func(bool) {}
static_assert(has_func<bool>); // Error
My first question: Why exactly this is happening? I suspect it has to do with ADL, because even though everything is in the global namespace, the global namespace is still a namespace, and bool, being built-in, is not technically in that namespace. So somehow I need to express the concept in terms of some user-defined type that captures the behavior of bool.
My second question: What is a good workaround to defer name lookup on my function? My fallback (since I can't forward-declare in my actual code) is to redefine the concept as std::same_as<T, bool> || ...
. However, I'm wondering if there's a more general hack to defer name lookup to the point where the concept is used. I've tried things like using requires(std::type_identity_t<T> t)
, but it doesn't help.
update:
I don't have a hack to work around the problem, but I understand that it is being caused by the dependent name lookup rules. Specifically:
- non-ADL lookup examines function declarations with external linkage that are visible from the template definition context
- ADL examines function declarations with external linkage that are visible from either the template definition context or the template instantiation context
So the two things I've tried are:
Calling some other function
dofunc
with an extra argument of a type that triggers ADL. That sort of works, except this function needs to callfunc
, so the placement of thedofunc
function now becomes tricky forfunc
to be in scope. It doesn't really improve things.Using some kind of proxy type with an operator bool, but this doesn't work because it doesn't trigger ADL of the function I want to call.
It may be that what I want to do is impossible on purpose, to avoid situations where the concept means something different in different contexts. Of course, with ADL the concept still might be different in different contexts, depending on what is in scope at the time the concept is used, so I still don't really get the justification for this restriction.