I'd like to have a class template with parameters with and without defaults. And I'd like a parameter with a default to be able to be deduced from the constructor. An example:
template <typename T, typename Compare = std::less<T> >
struct foo {
foo(const Compare& c = Compare{}) { }
};
foo<int> x; // Works, c = std::less<int>{}
foo<int, std::greater<int>> y; // Works, c = std::greater<int>{}
auto cmp = [](const int&, const int&){ return true; };
foo<int, decltype(cmp)> z(cmp); // Works (with C++20), c = cmp
foo<int> bad(cmp); // Fails
The issue with the bad code seems to be that since only template parameter T
is provided, the default of std::less<int>
is used for Compare
. This makes the constructor's signature foo(const std::less<int>&)
, which is not compatible with the call of foo(<lambda>)
, as the lambda can't be converted to std:less<int>
.
If another argument is added to the constructor, so that all template parameters can be deduced from constructor arguments, then it works.
template <typename T, typename Compare = std::less<T> >
struct bar {
bar(T, const Compare& c = Compare{}) { }
};
bar z2(int{}, [](){return true;});
While it might seem the issue is that the default value for the Compare
parameter is taking precedence over what could be deduced, that's not the case.
The real issue is this:
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.
Once a single template parameter is specified, no deduction will take place. Even though it would seem it is still possible to determine what the remaining template parameters would be.
Without a default value for a template parameter, the compiler with fail earlier with an diagnostic about an incorrect number of template parameters. So this limitation can not be observed without also using default values for template parameters.
In summary, at instantiation, class template parameters must be fully known. This can be done by (in order of priority if more than one applies):
These options are not allowed:
make_foo
works.
template,class T>
struct tag_t{};
template<class T>
constexpr tag_t<T> tag={};
template <typename T, typename Compare = std::less<T> >
struct foo {
foo(tag_t<T>, const Compare& c = Compare{}) { }
foo(const Compare& c = Compare{}) { }
};
also works:
foo f(tag<int>, cmp);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.