Reputation: 415
I'm trying to create a class called Sink
which will create a pointer to the class you pass in, this is to wrap an api in RAII.
In the full version of this code, the custom class also inherits from another class, and there is static assets to check this. The pointer is also passed to the api.
But to keep it simple I've removed this.
This is the error I get from cpp.sh
In function 'int main()':
43:30: error: no matching function for call to 'Sink<OneArg>::Sink(int)'
43:30: note: candidate is:
10:5: note: Sink<CustomSink, Args>::Sink(Args&& ...) [with CustomSink = OneArg; Args = {}]
10:5: note: candidate expects 0 arguments, 1 provided
Code:
#include <string>
#include <iostream>
#include <memory>
#include <utility>
template<typename CustomSink, typename... Args>
class Sink
{
public:
Sink(Args&&... args)
{
_ptr = std::make_unique<CustomSink>(std::forward<Args>(args)...);
}
~Sink()
{
}
private:
std::unique_ptr<CustomSink> _ptr;
};
//////////////////////////////////////////////////////////////////////
class NoArg
{
public:
NoArg() {};
~NoArg() {};
};
class OneArg
{
public:
OneArg(int a) {
std::cout << a << '\n';
};
~OneArg() {};
};
//////////////////////////////////////////////////////////////////////
int main(){
Sink<NoArg> noArgSink;
Sink<OneArg> oneArgSink(5);
return 0;
}
Upvotes: 4
Views: 283
Reputation: 3707
According to your design, you have to write:
Sink<OneArg, int> oneArgSink(5);
Indeed, number and type of arguments of constructor are determined by variadic part of your template (typename ... Args
). But this part is attached to class, not to constructor. So you have to specify it when you instantiate your template.
Otherwise, if you want to respect your instantiation and let compiler determines template arguments, you have to move variadic part of your template to constructor (see Jarod42 answer, or NathanOliver's one)
Upvotes: 3
Reputation: 699
The way you declared the class Sink
means you need to specify Args...
at template instantiation.
That means if you declare Sink<OneArg>
, you are effectively making Args...
empty and the constructor doesn't expect any arguments. That is why the compiler complains about passing an argument to constructor.
You only need the variadic template in the constructor. This way you can even accomodate classes having multiple constructors with different number of arguments.
template<typename CustomSink>
class Sink
{
public:
template<typename... Args>
Sink(Args&&... args)
{
_ptr = std::make_unique<CustomSink>(std::forward<Args>(args)...);
}
~Sink()
{
}
private:
std::unique_ptr<CustomSink> _ptr;
};
Upvotes: 1
Reputation: 180595
Your issue here is with the placement of the template type Args
. Right now you have Args
in the class tempalte so
Sink<OneArg> oneArgSink(5);
Says Args
is empty so Sink
s constructor expects no arguments. What you need to do is move Args
to the constructor by making it a template. That gives you
template<typename... Args>
Sink(Args&&... args)
{
_ptr = std::make_unique<CustomSink>(std::forward<Args>(args)...);
}
And now the constructor will deduce the arguments passed to it instead of having to specify it when you declare the class.
Upvotes: 5
Reputation: 217275
The template arguments of the constructor should be moved from the class to the templated constructor:
template<typename CustomSink>
class Sink
{
public:
template <typename... Args>
Sink(Args&&... args)
{
_ptr = std::make_unique<CustomSink>(std::forward<Args>(args)...);
}
private:
std::unique_ptr<CustomSink> _ptr;
};
Upvotes: 5