2

According to the C++ docs, std::copyable_function has two overloaded ctors as follows:

template<class T, class... CArgs>
explicit copyable_function(std::in_place_type_t<T>, 
    CArgs&&... args);

template<class T, class U, class... CArgs>
explicit copyable_function(std::in_place_type_t<T>, 
    std::initializer_list<U> il, CArgs&&... args);

Note that both ctors are required to construct its underlying callable object using direct-non-list-initialization. Consider the following code:

struct Functor {
    Functor(int, int) {
    }

    Functor(std::initializer_list<int>) {
    }

    void operator()() const {
    }
};

std::copyable_function(std::in_place_type<Functor>, 1, 2) will call Functor::Functor(int, int) rather than Functor::Functor(std::initializer_list<int>).

If we want to call Functor::Functor(std::initializer_list<int>), just use std::copyable_function(std::in_place_type<Functor>, std::initializer_list{1, 2}).

Both cases are intuitive and no ambiguity. So, my question is:

Why do we need the second ctor while the first one is enough?

1 Answer 1

10

{..} has no types and can only be deduced in few cases:

  • std::initializer_list<T>
  • const T(&)[N]

CArgs&& is not one of them so cannot deduce {1, 2} as std::initializer_list<int>

The second overload is needed.

You can see yourself with simplified example:

template <typename T>
void foo(T&&) {}

int main() {
    foo({1, 2}); // Error
}

Demo

Sign up to request clarification or add additional context in comments.

7 Comments

What if the std::initializer_list<T> parameter is not the first one? say, Functor(int, std::initializer_list<int>), it seems the second overload is just for the special case (though it is in most cases).
Position of parameter doesn't change anything for template deduction in that regard. Note that there are no deduction involved with std::initializer_list<int> ;-)
Demo with void foo(int, T&&)
What I mean is the second overload is just for the special case; otherwise we still have to use the explicit conversion like std::copyable_function(std::in_place_type<Functor>, std::initializer_list<int>{1, 2}). If we have to use the explicit conversion, then the second is still technically unnecessary.
std::copyable_function(std::in_place_type<Functor>, std::initializer_list<int>{1, 2}) would indeed take the first overload, but it is more verbose, as we have to be explicit about std::initializer_list.
Can I think the second overload is a syntax sugar just for the most cases (i.e. std::initializer_list<T> is the first parameter)?
I would says yes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.