1

C++ has an obnoxious limitation that it is impossible to pass overloaded functions to templates, for example std::max can not be nicely used with std::transform.

I was thinking that it would be nice if concepts could solve this, but in my attempts I hit the same issue. It looks like concepts are not able to constrain the template based on predicate on function type.

Example:

#include <type_traits>
#include <iostream>
#include <boost/callable_traits/args.hpp>
namespace ct = boost::callable_traits;

template <typename Fn>
concept Fn1 =  std::tuple_size<ct::args_t<Fn>>::value == 1;

template <typename Fn>
concept Fn2 =  std::tuple_size<ct::args_t<Fn>>::value == 2;

template<Fn1 Fn>
auto make(Fn f){
    return 1;
}

template<Fn2  Fn>
auto make(Fn f){
    return 2;
}

auto fn(int a){
}

auto fn(int a, float b){
    return 2;
}

int main() {
    std::cout << make(fn) << std::endl;
    std::cout << make(fn) << std::endl;
}

notes:

  • I know about different solutions to this problem, this is just an example problem to ask specifically if this can be done with concepts.
  • I know that just dispatching on arity is primitive, e.g. predicate should also return bool, etc.
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    How is *anyone* meant to distinguish `make(fn)` from `make(fn)`? – Caleth Dec 07 '21 at 16:57
  • @Caleth you are asking about readability or how could compiler do it? – NoSenseEtAl Dec 07 '21 at 17:02
  • 2
    @NoSenseEtAl: Both. On a token-by-token level, they are identical. Therefore, they must result in identical calls. I mean... what *else* would you expect to happen? – Nicol Bolas Dec 07 '21 at 17:06
  • Ah, I reduced my example too much... :) Originally I wanted to pass fn to factory that makes a struct with member funs that are from overload set... but same idea, those member fns would be invoking specific arity functions from original overload set(based on number of arguments passed to member fn of struct)... – NoSenseEtAl Dec 07 '21 at 17:14
  • @NoSenseEtAl Maybe edit the question to be something sensible? The question as presented makes no sense. – Barry Dec 07 '21 at 18:09
  • @Barry would you be ok with me deleting it, now I found your old proposal for passing overload sets so I guess that answers my question? And I dont want to make existing answer useless... – NoSenseEtAl Dec 07 '21 at 19:33
  • @Barry now looking back this looks idiotic, but this is what I was originally aiming for: https://godbolt.org/z/5vEYfborE – NoSenseEtAl Dec 07 '21 at 19:56

1 Answers1

2

In order for the language to consider whether a type fulfills a concept, C++ must first deduce the type of the argument and plug it into the template function. That deduction cannot happen because the argument is a name representing a function with multiple overloads. So concepts don't even get a chance to work.

So long as an expression consisting of the name of an overloaded function cannot undergo template argument deduction, what you're trying to do cannot work.

And even if it did work, it still wouldn't work. In this hypothetical, fn fulfills both concepts. And while overloading based on concepts is a thing, it's a thing based on comparing the atomic constraints to look for similarities to see which is more constrained. But their atomic constraints are unrelated (as far as C++ is concerned). Thus, both would be considered equally as valid, and therefore concept overloading would fail.

You're just going to have to do what everyone else does: create a lambda.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982