Лямбда преобразуется в bool вместо определения типа указателя на функцию

Я хотел реализовать перегрузку для operator<< это позволило мне вызвать данную функцию и вывести результат. Поэтому я написал перегрузку, но преобразование в bool выбрано, и при написании функции оно не компилируется.

РЕДАКТИРОВАТЬ: Знайте, что я не хочу вызывать лямбда, но вместо этого передать его в функцию, где он должен быть вызван с созданным по умолчанию списком параметров.

Я добавил свой код:

#include <iostream>

template<typename T>
void test(T *) {
    std::cout << "ptr" << std::endl;
}
template<typename T>
void test(bool) {
    std::cout << "bool" << std::endl;
}
template<typename Ret, typename ...Args>
void test(Ret(*el)(Args...)) {
    std::cout << "function ptr\n" << el(Args()...) << std::endl;
}

template<typename Char_T, typename Char_Traits, typename Ret, typename ...Args>
std::basic_ostream<Char_T, Char_Traits>& operator<<(
      std::basic_ostream<Char_T, Char_Traits> &str, Ret(*el)(Args...)) {
    return str << el(Args()...);
}

int main() {
    std::boolalpha(std::cout);
    std::cout << []{return 5;} << std::endl; // true is outputted
    test([]{return 5;}); // will not compile
}

Я использую gcc 7.3.1 с флагом версии -std=c++14,

РЕДАКТИРОВАТЬ: сообщение об ошибке:

main.cc: In function ‘int main()’:
main.cc:25:23: error: no matching function for call to ‘test(main()::<lambda()>)’
     test([]{return 5;});
                       ^
main.cc:5:6: note: candidate: template<class T> void test(T*)
 void test(T *) {
      ^~~~
main.cc:5:6: note:   template argument deduction/substitution failed:
main.cc:25:23: note:   mismatched types ‘T*’ and ‘main()::<lambda()>’
     test([]{return 5;});
                       ^
main.cc:9:6: note: candidate: template<class T> void test(bool)
 void test(bool) {
      ^~~~
main.cc:9:6: note:   template argument deduction/substitution failed:
main.cc:25:23: note:   couldn't deduce template parameter ‘T’
     test([]{return 5;});
                       ^
main.cc:13:6: note: candidate: template<class Ret, class ... Args> void test(Ret (*)(Args ...))
 void test(Ret(*el)(Args...)) {
      ^~~~
main.cc:13:6: note:   template argument deduction/substitution failed:
main.cc:25:23: note:   mismatched types ‘Ret (*)(Args ...)’ and ‘main()::<lambda()>’
     test([]{return 5;});

3 ответа

Решение

Ваша проблема здесь в том, что вычитание аргумента шаблона выполняется только для фактического аргумента, переданного test, Это сделано не для всех возможных типов, в которые аргумент может быть преобразован. Это может быть бесконечное множество, так что это явно не пойдет.

Итак, Template Argument Deduction выполняется для реального лямбда-объекта, который имеет невероятный тип класса. Таким образом, вычет для test(T*) терпит неудачу, поскольку лямбда-объект не является указателем. T не может быть выведено из test(bool) очевидно. Наконец, вычет не удается для test(Ret(*el)(Args...)) поскольку лямбда-объект также не является указателем на функцию.

Есть несколько вариантов. Вам может даже не понадобиться шаблон, вы можете принять std::function<void(void)> и полагаться на тот факт, что он имеет шаблонный конструктор. Или вы могли бы просто взять test(T t) аргумент и назвать его как t(), T теперь выведет к фактическому лямбда-типу. Наиболее причудливое решение, вероятно, использует std::invoke и принятие шаблона vararg list.

Даже если лямбды без захвата имеют неявное преобразование в указатели функций, шаблоны функций должны точно совпадать, чтобы вывод был успешным, преобразования не будут выполняться.

Следовательно, самое простое решение - заставить преобразование +

int main() {
    std::boolalpha(std::cout);
    std::cout << []{return 5;} << std::endl; // true is outputted
    test(+[]{return 5;}); 
    //   ^
}
template<typename T>
void test(bool) {
    std::cout << "bool" << std::endl;
}

Шаблон не нужен. На самом деле вы перегружаете функции, а не шаблоны. Замени это

void test(bool) {
     std::cout << "bool" << std::endl;
}

Теперь ваш образец будет скомпилирован.

Другие вопросы по тегам