2

If you try something relatively simple in C++20 it implodes with unhelpful error message spam.

int main() {
    auto as = std::vector{1,3,24,};
    auto bs = std::vector{1,4,10};
    auto cs = std::vector<float>{};
    std::ranges::transform(as, bs, std::back_inserter(cs), std::max);
    std::ranges::copy(cs, std::ostream_iterator<float>(std::cout, " "));
}

Reason is that std::max is templated function, so it just does not work. This can be easily worked around by making argument a lambda or making a small helper functor.

But I was wondering if C++ concepts could be used to tell what template instantiation what we want? For example, we hack some requires statement that says that if functor argument is template then the template arguments of that functor must match the container value_type.

I doubt that is possible, since I think that exact type of functor must be known before template overload resolution and constraint cheks kick in, in other words, there is no "backpropagation" of information from concepts to callsite.

But I am not sure so I decided to ask.

If you want to know what I have tried, here is my code, but it is very broken, I am incapable of writing template template arguments code...

But anyways I hope it illustrates the idea...

template<typename C, typename F>
struct xtransform
{
    xtransform(C& c, F f) : c_(c), f_(f){}
    void operator()(){
    }
    C c_;
    F f_;
};
template<typename C, template<typename> typename F, typename FArg>
requires requires {
    std::is_same_v<typename C::value_type, FArg>;
}
struct xtransform<C, F<FArg>>
{
    xtransform(C& c, F<FArg> f) : c_(c), f_(f){}
    void operator()(C& c, F<FArg> f){
    }
    C c_;
    F<FArg> f_;
};
5
  • 3
    I doubt that is possible, since I think that exact type of functor must be known before template overload resolution and constraint cheks kick in Correct. The name std::max names a template. You can't get a concrete thing from that. I think it could be fixed if an overload was added that take a template template parameter. Commented Mar 31, 2021 at 19:42
  • @NathanOliver is your last sentence talking about language changes or changes to my code? Because that is what I think I tried to do. Commented Mar 31, 2021 at 19:48
  • 2
    Not a language change, but a standard library change. At least I think it could be fixed via a library change. I'm having some doubts as I still think you need a concrete object passed to the function. Commented Mar 31, 2021 at 19:58
  • @NathanOliver: Template template parameters must be class/alias templates; function templates just aren’t a thing other than during (their own!) overload resolution. Commented Mar 31, 2021 at 22:08
  • @NathanOliver I think the easiest library change would be make std::max a neibloid, although that breaks existing code. Commented Apr 1, 2021 at 8:37

1 Answer 1

3

No.

std::max names a template function. Template function names with incomplete template parameter lists are turned into functions during overload resolution. Overload resolution requires converting the function name to a pointer to a fixed signature, or invoking with ()s.

Concepts don't help.

Now you can use an older trick; have an overload of your function that takes a function pointer in a non-deduced context. When I write hand-rolled std function-like types, I add in a R(*)(Args...) overload for exactly this reason; that permits overload resolution when there is an overload whose types match exactly.

But that isn't concept based.

Sign up to request clarification or add additional context in comments.

2 Comments

Hi, did you ever wrote up the example of your solution in some other SO answer?
@nosense probably? I have written a pile of std function replacements here. Don't know which however.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.