Igor R.
Igor R.

Reputation: 15075

Function template call: type deduction and empty brace-enclosed initializer list

Consider the following function template calls:

#include <optional>

template <class T = int>
void f(std::optional<T>);

int main() {
  f(1);  // (1)
  f({}); // (2)
}

The first call (1) doesn't compile: type-deduction should occur before any type-conversion of the arguments can take place, but T cannot be deduced in this context.

However, (2) does compile, and the default template argument is used. Why?

Upvotes: 3

Views: 119

Answers (2)

Aname
Aname

Reputation: 610

As an addition to dick's answer and the comments, trying to be more explicit.

To my understanding, std::optional is not like, for example ..., : it always must must be there, even if it does not contain a value, the argument must be there.

Your example (2) constructs an empty std::optional, not "just" an empty int (which would be 0, so a value). Your (1) asks the compiler to create an optional using an int : there is no constructor for that https://en.cppreference.com/w/cpp/utility/optional/optional, putting brackets will use the default std::optional constructor with what's in the brackets though.

Based on your example, this piece

#include <optional>
#include <iostream>
template <class T=int>
void f(std::optional<T> a){
    if(a.has_value())
        std::cout<<a.value()<<std::endl;
};

int main() {
  f(std::optional<int>(1));  // (1)
  f({}); // (2)
  f(std::optional<std::string>()); // (2')
  f({3});  // (3)
  f({3.1});  // (4)
  f(std::optional<float>(3.11));  // (5)
  f(std::optional<std::string>("explicixt")); // (6)
}

outputs

1
3
3
3.11
explicixt

(1) is a valid version of what you wanted, so is {3}

(2) and (2') are nearly equivalents, the not-present optional value has a different type.

Note that (4) outputs 3 : the optional<int> constructor is used, since that's your template preference.

(5) and (6) show how you can/must explicit the optional type you pass to the function, otherwise optional<int> constructor will be used.

Overall, these few lines show that you must pass a std::optional<something>, not just a <something>, not nothing : std::optional always is something, whether is does or noes not contain a value.

I hope it helps; Regards.

Upvotes: -1

duck
duck

Reputation: 2448

During template argument deduction from a function call, braced-init-list arguments are treated specially: in particular, type deduction is never attempted from an empty initializer list argument. For the call to be well-formed, arguments for template parameters in the type of the corresponding function parameter must be obtained elsewhere (for example, by deduction from other function arguments, or, as in this case, from default template arguments). This is called a non-deduced context; see [temp.deduct.type] for details.

A plain int argument, on the other hand, gets no such treatment: in the call f(1), deduction is attempted as usual for the int/std:optional<T> argument/parameter pair, and fails (since there is no T that makes the types match) before reaching the step where default template arguments are considered, making the call ill-formed.

Upvotes: 1

Related Questions