Tom de Geus
Tom de Geus

Reputation: 5965

Infer template argument for pointer to function

For the following code:

#include <iostream>
#include <vector>

template <class T>
inline auto foo(const T* A)
{
    return A[0];
}

template <class T>
struct impl
{
    template <typename F>
    static auto bar(const T& A, F func)
    {
        return func(&A[0]);
    }
};

int main()
{
    std::vector<size_t> A = {2, 3, 4};
    std::cout << impl<decltype(A)>::bar(A, &foo) << std::endl;
    return 0;
}

I get (with C++14 using clang on macOS) that

main.cpp:23:18: error: no matching function for call to 'bar'
    std::cout << impl<decltype(A)>::bar(A, &foo) << std::endl;
                 ^~~~~~~~~~~~~~~~~~~~~~
main.cpp:14:17: note: candidate template ignored: couldn't infer template argument 'F'
    static auto bar(const T& A, F func)
                ^
1 error generated.

My question is: Why? How can I fix it?

Upvotes: 2

Views: 127

Answers (3)

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122133

The issue is actually from foo not from impl. foo is a template and you will an error also for:

auto x = &foo;

source>:23:15: error: unable to deduce 'auto' from '& foo'
   23 |     auto x = &foo;
      |               ^~~
<source>:23:15: note:   couldn't deduce template parameter 'auto'

I am not suggesting this a the "right" solution, it is just to show how in principle you can get a pointer to the right instantiation of foo:

int main()
{
    std::vector<size_t> A = {2, 3, 4};
    std::cout << impl<decltype(A)>::bar(A,
        static_cast<size_t(*)(const decltype(A)::value_type*)>(&foo)) << std::endl;
    return 0;
}

Or the more readable (thanks to @StoryTeller):

int main()
{

    std::vector<size_t> A = {2, 3, 4};
    //auto x = &foo<decltype(A)::value_type>;
    std::cout << impl<decltype(A)>::bar(A,&foo<decltype(A)::value_type>) << std::endl;
    return 0;
}

Note that gcc does emit the same error as for the original. This appears to be a bug in gcc. Removing the // from the line above magically makes it compile also with gcc even though x is not used. The bug seems to be fixed in gcc-trunk: https://godbolt.org/z/noqGEP (see also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54111).

Upvotes: 2

Marek R
Marek R

Reputation: 37607

This should work (without lambda):

#include <iostream>
#include <vector>

template <class T>
auto foo(const T* A)
{
    return A[0];
}

template <class T>
struct impl
{
    template <typename F>
    static auto bar(const T& A, F func)
    {
        return func(&A[0]);
    }
};

int main()
{
    std::vector<size_t> A = {20, 3, 4};

    std::cout << impl<decltype(A)>::bar(A, &foo<size_t>) << std::endl;

    return 0;
}

but gcc fails for some reason (clang and msvc are fine with that).

Upvotes: 1

NathanOliver
NathanOliver

Reputation: 180500

foo is the name of a template, not the name of a function. bar requires a type, not a template. Fortunately lambda expressions are a fast easy way to wrap generic function into an object type that can be known and do the dispatch for you. That would give you

std::cout << impl<decltype(A)>::bar(A, [](const auto& var){ return foo(var); }) 
          << std::endl;

Upvotes: 3

Related Questions