5

I have some code here

template<typename T, std::size_t size, typename funcType>
struct foo
{
public:

    foo(const funcType& func) : m_func(func) {}
    ~foo() {}
    
    void m_call() { m_func(); }

private:
    const funcType& m_func;

    T x[size];
};

void printString() { std::cout << "some string\n"; }

I can create an object

foo<int, 3, void(*)()> someObject(printString);

or

foo<int, 3, decltype(printString)> someObject(printString);

but when I try to do this:

foo<int, 3> someObject(printString);

I get this error on g++ 10.2

error: wrong number of template arguments (2, should be 3)
 foo<int, 3> someObject(printString);
           ^
note: provided for 'template<class T, long unsigned int size, class funcType> struct foo'
 struct foo
       

Why cant I do this? Doesn't the compiler know what type printString is?

And if I change the foo to

template<typename funcType>
struct foo
{
public:

    foo(const funcType& func) : m_func(func) {}
    ~foo() {}
    
    void m_call() { m_func(); }

private:
    const funcType& m_func;
};

I can create it normally

foo someObject(printString);

Am I missing something?

cigien
  • 57,834
  • 11
  • 73
  • 112

2 Answers2

3

According to cppreference:

Class template argument deduction is only performed if no template argument list is present. If a template argument list is specified, deduction does not take place.

which your last experiment confirms. To deduce funcType you also need to provide other templated types in constructor to not provide any template argument list.

You can bind other templates with constructor for example using this construct:

#include <iostream>

template<typename T, std::size_t size, typename funcType>
struct foo
{
public:
    foo(T (&arr)[size], const funcType& func) : m_func(func) {}
    ~foo() {}

    void m_call() { m_func(); }

private:
    const funcType& m_func;
    T x[size]{};
};

void printString() { std::cout << "some string\n"; }


void test() {
    foo someObject("test", printString);

}

godbolt

Jakub Piskorz
  • 1,023
  • 12
  • 21
  • This doesn't really answer the question. CTAD requires all the template parameters to be deduced which is not possible for the first 2 parameters of the class. – cigien Jan 22 '21 at 08:33
  • 1
    Well, the question is "Why can't I deduce one of the class template arguments?", answer is you can't because you provide partial information, either provide by hand all, or none to even start deducing. Which I agree will fail, as constructor only creates guide for last template argument. – Jakub Piskorz Jan 22 '21 at 09:12
  • 1
    Ah, I see. You're right, this does address the question. I misread it, since you hadn't shown an alternative, but that's perfectly fine. – cigien Jan 22 '21 at 09:26
  • It’s worth saying *why* this restriction exists, which is backward compatibility with class templates with default template arguments. – Davis Herring Jan 22 '21 at 23:38
1

Use template function to create the object and to deduct missing template arguments from function call. Something like this.

template<typename T, std::size_t Size, typename FunctionT>
foo<T, Size, FunctionT> create_foo(const FunctionT &func) {
    return foo<T, Size, FunctionT>(func);
} 

auto foo_obj = create_foo<int, 3>(printString);
4xy
  • 3,494
  • 2
  • 20
  • 35