почему значение type_identity имеет значение?

#c #templates #metaprogramming

Вопрос:

Я знаю, что lambda объект-это не std::function объект, поэтому я понимаю, что это не сработает:

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

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

но почему это сработает, если я добавлю идентификатор типа для его обертывания?

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

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([](int a){ }, 1);
}

 

насколько я могу видеть, эти двое выглядят почти одинаково, и это даже прошло static_assert , что означает, что они одинаковы std::is_same .
но компилятор просто так не думает. это сказало мне, что в первом коде лямбда не может соответствовать какой-либо функции, в то время как последняя может.

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

итак, мой вопрос: почему они ведут себя по-другому? что неявно делал type_identity?

Ответ №1:

Прежний код не работает, потому что неявное преобразование (из лямбда в std::function ) не будет учитываться при вычитании аргумента шаблона, что приводит к ошибке вычитания параметра шаблона из args_t 1-го параметра функции function_ .

Вычет типов не учитывает неявные преобразования (кроме перечисленных выше корректировок типов): это задача по разрешению перегрузки, которая выполняется позже.

Последний код работает из-за не выведенного контекста:

В следующих случаях типы, шаблоны и значения, не относящиеся к типу, которые используются для составления P, не участвуют в выводе аргументов шаблона, а вместо этого используют аргументы шаблона, которые были либо выведены в другом месте, либо явно указаны.

  1. Спецификатор вложенного имени (все слева от оператора разрешения области ::) типа, указанного с использованием квалифицированного идентификатора:

При использовании type_identity 1-го параметра функции function_ исключается вычет аргумента шаблона; и args_t может быть выведен из 2-го параметра функции args , тогда он работает нормально.

КСТАТИ: Начиная с C 20, у нас есть std::type_identity .