0

For example, I wrote the following piece of code:

struct default_type {};

template <typename U = default_type> auto func(int x, U u) {
    if constexpr(is_same<U, default_type>::value) {
        return x * 2;
    }
    else {
        return u(x);
    }
}

int main() {
    cout << "5 + 3 = " << func(5, [](int x){ return x + 3; }) << endl;
    cout << "5 * 2 = " << func(5) << endl;
}

But this gives a compiler error of "no matching function for call to 'func(int)'". What would be the correct way to do this?

Niko
  • 257
  • 1
  • 8

1 Answers1

3

You have provided the default type, but no default value. The function still requires two parameters, and the default_type will be overridden by the deduced one, making it useless.

Do this:

template <typename U = default_type>
auto func(int x, U u = {}) { ... } // (1)

I can't support this by the exact standardese, but in short: U is acquired at the template argument deduction stage (having no deduction rule - not considering default function arguments, therefore defaulted), which comes before the overload resolution stage, at which point the default function arguments are interpreted, already knowing the type of U, which makes the list initialization valid.


Yes, you could as well write:

template <typename U = default_type>
auto func(int x, U u = U()) { ... } // (2)

or

template <typename U = default_type>
auto func(int x, U u = default_type()) { ... } // (3)

with slightly different semantics when explicitly providing the U type (which I don't suppose you will):

func<not_the_default_type>(5);

Here, (1) and (2) are equivalent, but in (3), the construction of U happens via constructing a default_type temporary, which is then converted to not_the_default_type.


Lastly, quite the opposite mistake would be to expect U to be deduced here:

template <typename U>
auto func(int x, U u = default_type()) { ... }

which is not the case. See this Q&A.

LogicStuff
  • 19,397
  • 6
  • 54
  • 74
  • 1
    In a side note, if you give the `default_type` struct an `operator()` implementation, then you don't need the `constexpr` at all: `struct default_type { int operator()(int x) { return x * 2; }}; template auto func(int x, U u = {}) { return u(x); }` – Remy Lebeau Apr 23 '19 at 22:40