why would type_identity make a difference?
Asked Answered
V

1

14

I know that a lambda object is not a std::function object, so I understand that this would not work:


template <typename ...args_t>
void func(std::function<void(args_t...)> function_, args_t ...args){
    /// do something here
}

void test() {
    func([](int a){ }, 1);
}

but why would it work if I add a type_identity to wrap it?

template <typename T>
struct type_identity {
    using type = T;
};
template <typename T>
using type_identity_t = typename type_identity<T>::type;

template <typename... args_t>
void func_wrapped(type_identity_t<std::function<void(args_t...)>> function_, 
                                  args_t ...args) {
    static_assert(std::is_same< std::function<void(args_t...)>,
                                 type_identity_t<std::function<void(args_t...)>>
                              >::value,
                  "different type");
    /// do something here
}

void test() {
    func_wrapped([](int a){ }, 1);
}

as far as I can see, these two look pretty much the same, and it even passed the static_assert which means they are the same to std::is_same. but the compiler just doesn't think so. it told me that the in former code, lambda cannot match any function, while the latter one can.

error: no matching function for call to ‘func(test()::<lambda(int)>, int)’

so, my question is: why do they behave differently? what did type_identity do implicitly?

Violet answered 6/8, 2021 at 2:36 Comment(0)
M
14

The former code doesn't work because implicit conversion (from lambda to std::function) won't be considered in template argument deduction, which causes deduction of template parameter args_t on the 1st function parameter function_ failing.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

The latter code works because of non-deduced context:

In the following cases, the types, templates, and non-type values that are used to compose P do not participate in template argument deduction, but instead use the template arguments that were either deduced elsewhere or explicitly specified.

  1. The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

With the usage of type_identity the 1st function parameter function_ is excluded from template argument deduction; and args_t could be deduced from the 2nd function parameter args, then it works fine.

BTW: Since C++20 we have std::type_identity.

Manard answered 6/8, 2021 at 2:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.