Home > Enterprise >  Templated function type lost when embedding templates
Templated function type lost when embedding templates

Time:01-07

When playing around with C 20's concepts, I've found that when making a concept describing how a function should be (i.e. T must be a callable function that takes a size_t as argument), then using that concept in another template, the type of the function seems to be "lost". I don't really have a good way of phrasing it, so here's the code:

template<typename Func, typename ... Args>
concept FuncWithArgs = requires (Func f, Args... args) { f((size_t)1, args...); };

template<FuncWithArgs Func, typename ... Args>
void Foo(const Func& f, size_t i = 0, Args... args)
{
    f(i, args...);
}

Then calling it with:

void MyFunc(size_t i, const std::string& s)
{ 
    std::cout << i << ", " << s << std::endl; 
}

int main(int argc, char **argv)
{
    Foo(MyFunc, 1, "asdf");

    return 0;
}

Gives the following compilation error messages (using GCC 11.2 with -std=c 20, https://godbolt.org/z/dvs9j77o3):

<source>: In function 'int main(int, char**)':
<source>:20:8: error: no matching function for call to 'Foo(void (&)(size_t, const string&), int, const char [5])'
   20 |     Foo(MyFunc, 1, "asdf");
      |     ~~~^~~~~~~~~~~~~~~~~~~
<source>:8:6: note: candidate: 'template<class Func, class ... Args>  requires  FuncWithArgs<Func> void Foo(const Func&, size_t, Args ...)'
    8 | void Foo(const Func& f, size_t i = 0, Args... args)
      |      ^~~
<source>:8:6: note:   template argument deduction/substitution failed:
<source>:8:6: note: constraints not satisfied
<source>: In substitution of 'template<class Func, class ... Args>  requires  FuncWithArgs<Func> void Foo(const Func&, size_t, Args ...) [with Func = void(long unsigned int, const std::__cxx11::basic_string<char>&); Args = {const char*}]':
<source>:20:8:   required from here
<source>:5:9:   required for the satisfaction of 'FuncWithArgs<Func>' [with Func = void()]
<source>:5:24:   in requirements with 'Func f', 'Args ... args' [with Args = {}; Func = void()]
<source>:5:59: note: the required expression 'f((size_t)(1), args ...)' is invalid
    5 | concept FuncWithArgs = requires (Func f, Args... args) { f((size_t)1, args...); };
      |                                                          ~^~~~~~~~~~~~~~~~~~~~
cc1plus: note: set '-fconcepts-diagnostics-depth=' to at least 2 for more detail

Looking closely at this, I first see [with Func = void(long unsigned int, const std::__cxx11::basic_string<char>&); Args = {const char*}], followed shortly after by [with Args = {}; Func = void()]. Why/how did Func change?

After more digging, I came up with a solution that works (https://godbolt.org/z/j6W7P895q):

template<typename Func, typename ... Args> 
requires requires (Func f, Args... args)    
{ 
        f((size_t)1, args...); 
}  
void Foo(const Func& f, size_t i = 0, Args... args) 
{
    f(i, args...);
}

Since the constraint used is identical to the one I tried doing with the concept, it leads me to believe that the type of the function is "lost" and that the concept itself is properly written.

Is it something that I am not doing right, a known issue, or a limitation of templates?

CodePudding user response:

When you write this:

template<typename Func, typename ... Args>
concept FuncWithArgs = requires (Func f, Args... args) { f((size_t)1, args...); };

template<FuncWithArgs Func, typename ... Args>
void Foo(const Func& f, size_t i = 0, Args... args)
{
    f(i, args...);
}

The declaration of Foo is shorthand for this:

template <typename Func, typename ... Args>
    requires FuncWithArgs<Func>
void Foo(const Func& f, size_t i = 0, Args... args)
{
    f(i, args...);
}

Which probably makes it clear what went wrong. You're requiring that the function is callable with no args (well, really 1 arg, the size_t), but that's not what you want. You want this:

template <typename Func, typename ... Args>
    requires FuncWithArgs<Func, Args...>
void Foo(const Func& f, size_t i = 0, Args... args)
{
    f(i, args...);
}

which can be written using this shorthand:

template <typename ... Args, FuncWithArgs<Args...> Func>
void Foo(const Func& f, size_t i = 0, Args... args)
{
    f(i, args...);
}
  •  Tags:  
  • Related