简体   繁体   中英

Deduce class template parameter with default

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):

  1. Fully specifying all parameters.
  2. Specifying no parameters, if all parameters can be deduced.
  3. Specifying no or some parameters, if all unspecified parameters have defaults.

These options are not allowed:

  • Specifying some parameters, if all unspecified parameters can be deduced.
  • Specifying some parameters, if all unspecified parameters can be deduced or have a default value.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM