3

I understand that with concepts, a constrained function (regardless how "loose" the constraint actually is) is always a better match than an unconstrained function. But is there any syntax to selectively call the unconstrained version of f() as in the sample code below? If not, would it be a good idea for compilers to warn about uncallable functions?

#include <iostream>

template <typename T> requires(true) 
void f() { std::cout << "Constrained\n"; }

template <typename T>
void f() { std::cout << "NOT Constrained\n"; }


int main() {
    f<int>();
}

https://godbolt.org/z/n164aTvd3

Chris Uzdavinis
  • 6,022
  • 9
  • 16
  • "*is there any syntax to selectively call the unconstrained version of f()*" And how would you want to select it? – Nicol Bolas Mar 29 '22 at 15:38
  • 1
    The definitions already look ill-formed, NDR. The template heads are functionally equivalent but not equivalent. https://www.eel.is/c++draft/temp#over.link-7 – user17732522 Mar 29 '22 at 15:39
  • @user17732522 Are you sure that clause applies? "If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required." In my example, the validity does _not_ depend on the two constructs being equivalent. Further, https://www.eel.is/c++draft/temp#over.link-example-4 shows 2 "identical" functions that differ only in constraints yet are explicitly labelled as OK. (Though not templates themselves, but members of a template class.) – Chris Uzdavinis Mar 29 '22 at 16:38
  • @NicolBolas How would I want to is irrelevant. :) I was asking if there is a way, regardless if it's how I'd like it to happen. I'm curious about how this part of the language works. It is not a reflection of a problem I'm working around. But, for example, one can use explicit namespace qualification to find otherwise hidden functions in namespaces and perhaps there is a way to do something similar to select a specific function. – Chris Uzdavinis Mar 29 '22 at 16:40
  • 1
    @ChrisUzdavinis Your example would be ill-formed if the two template heads were equivalent since you would then have two definitions for the same entity, so the validity does depend on it. Good question about the example. I am not exactly sure why it is not also considered ill-formed, NDR. – user17732522 Mar 29 '22 at 17:23
  • 1
    @ChrisUzdavinis I posted a question regarding that example from the standard [here](https://stackoverflow.com/questions/71667229/functional-equivalency-in-template-constraints-vs-member-function-constraints). – user17732522 Mar 29 '22 at 19:02

1 Answers1

4

Different overloads of a function are meant to all do the same thing. They may do it in different ways or on different kinds of objects, but at a conceptual level, all overloads of a function are supposed to do the same thing.

This includes constrained functions. By putting a constrained overload in a function overload set, you are declaring that this is a valid alternative method for doing what that overload set does. As such, if the constraint matches, then that's the function that should be called. Just like for parameters of different types in regular function overloading.

If you want to explicitly call an overload hidden by a constraint, you have already done something wrong in your design. Or more specifically, if some overload is completely hidden by one or more constrained overloads, you clearly have one more overload than you actually needed.

If the constraints match, the caller should be 100% fine with getting the constrained overload. And if this isn't the case, your design has a problem.

So no, there is no mechanism to do this. Just as there's no mechanism to bypass an explicitly specialized template and use the original version if your template arguments match the specialization.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 1
    I disagree with design problem and rational for all cases, for example, for template vs regular functions, it is possible `template void f(T) {/*..*/} void f(int n) { /*some stuff*/ f(n); }`. – Jarod42 Mar 29 '22 at 15:49
  • ADL puts names into an overload set, yet there are techniques to disable ADL lookups for symbols. I do not think my question is asking for something outrageous. That is, what I _want_ is not an issue here, nor is my design. (Truth is, I have no design, but am digging into requirements and trying to understand some corner cases.) I was asking if c++ allows this or not, independently of whether it's a good idea or not. – Chris Uzdavinis Mar 29 '22 at 16:45
  • Thank you for the final paragraph. That's the answer I was looking for. :) – Chris Uzdavinis Mar 29 '22 at 16:46