Reputation: 8636
#include <iostream>
using namespace std;
template <typename T>
class test {
public:
T value;
template <typename... Args, typename = decltype(T())>
test(Args... args): value(args...)
{
cout <<"ctor running\n";
}
template <typename... Args>
test(Args...) : value(1)
{
cout <<"ctor unspec running\n";
}
};
class t
{
public:
t() = delete;
explicit t(int) {}
};
int main()
{
test<t> h;
}
I am trying to call the second constructor
for the object created (h
). I do not know why I get this error:
prog.cc: In function 'int main()': prog.cc:45:13: error: call of overloaded 'test()' is ambiguous test<t> h; ^ prog.cc:25:5: note: candidate: 'test<T>::test(Args ...) [with Args = {}; T = t]' test(Args... args) ^~~~ prog.cc:19:5: note: candidate: 'test<T>::test(Args ...) [with Args = {}; <template-parameter-2-2> = t; T = t]' test(Args... args): value(args...) ^~~~
I tried to make the entire class t
private
but that did not fix it either. I want the second constructor
to run i.e. print `
"ctor unspec running"
What am I missing here? The first constructor
call should be SFINAed
away since typename = decltype(T())
wont work as t
can not be default constructed
but instead I get an ambiguous
call error.
Upvotes: 4
Views: 918
Reputation: 41780
SFINAE only happen with immediate context. Since T
is a template argument of the class and not the template argument of the function, it is not immediate context. That means it becomes an "hard" error. It's an hard error because no matter what argument you send to the template argument of the constuctor, it will always be an error.
A solution would be to add a template argument equal to T
, and use it to make SFINAE:
template <typename... Args, typename U = T, typename = decltype(U{})>
test(Args... args): value(args...)
{
cout <<"ctor running\n";
}
Since U
is immediate context, SFINAE is applied here.
With SFINAE, there is no ordering done. Every matching function is "equal", which means that if there is multiple matching function, there is no "better" one because it is constrained. So it would be a good idea to constrain the other one with a contrary constraint:
template <typename... Args, typename U = T,
std::enable_if_t<!std::is_default_constructible<U>::value>* = nullptr>
test(Args...) : value(1)
{
cout <<"ctor unspec running\n";
}
Upvotes: 5