ошибка вывода типа шаблона (std :: empty как предикат) - PullRequest
0 голосов
/ 28 мая 2018

У меня есть вектор векторов, и я хочу проверить, все ли они пусты.Используя стандартную библиотеку, я попытался:

#include <algorithm>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), std::empty);
}

Это привело к следующей ошибке в clang 7.0:

/ usr / bin /../ lib / gcc / x86_64-linux-gnu / 6.3.0 /../../../../ include / c ++ / 6.3.0 / bits / stl_algo.h: 508: 5: примечание: шаблон кандидата игнорируется: невозможно определить аргумент шаблона'_Predicate'

Я предполагаю, что это стандартное поведение из-за правил вывода типов.Но в любом случае, какой мне самый простой способ обойти это?

РЕДАКТИРОВАТЬ: Я принял ответ Rubenvb, потому что он дал простое и разумное объяснение, вместе с естественным обходным путем.all_of принимает предикат, который является функцией, функциональным объектом или лямбда-выражением.std :: empty не является ни одним из них, а шаблоном функции.При явном его создании мы получаем простую функцию, которая должна работать.Удивительно, но он все еще не компилируется на большинстве компиляторов, которые я пробовал.

Хорошо, давайте посмотрим:

на GCC 6.3, он компилируется просто отлично - https://godbolt.org/g/Pxta7C

, но на GCCиз транка это вызывает внутреннюю ошибку компилятора - https://godbolt.org/g/H6DHt5

Ни Clang из транка, ни MSVC 2017 не смогли его скомпилировать:

https://godbolt.org/g/819pbQ (Clang)

https://godbolt.org/g/ua5E8e (MSVC)

EDIT2: Судя по всему, Роберт Анджейюк тоже прав: причина, по которой компилятор не может справиться с этим, - неоднозначное разрешение перегрузки.std :: empty имеет 3 разных перегрузки.и два из них являются одинаково хорошими кандидатами: общий и список std :: initializer один.Я достиг аналогичных результатов со следующей минимальной версией:

#include <vector>

template<class T>
void foo(const T& t);

template<class T>
void foo(const std::initializer_list<T>& il);

template<class F>
void bar(F f);


int main()
{
   bar(foo<std::vector<int>>);
}

Однако есть одно отличие.Этот пример просто не компилируется в GCC из транка (вместо того, чтобы вызывать ICE).

Ответы [ 5 ]

0 голосов
/ 28 мая 2018

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

#include <algorithm>
#include <iterator>
#include <vector>

int main()
{
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), std::empty<std::vector<int>>);
}

Live-демонстрация (которая случайно приводит к сбою компилятора) .Также обратите внимание, что эта версия GCC, очевидно, нуждалась в #include <iterator>, хотя в ней явно указано, что std::empty должен входить, например, #include <vector> ...

0 голосов
/ 28 мая 2018

К сожалению, существует проблема с распознаванием перегруженных функций шаблона, так как std::all_of также является функцией шаблона.Лучшее объяснение: std :: function не может различить перегруженные функции

Таким образом, static_cast для правильного типа функции: bool ( * )( const std::vector< int >& ) требуется:

std::all_of( vv.begin(), vv.end(),
             static_cast< bool ( * )( const std::vector< int >& ) >( std::empty ) );

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

Вспомогательная функция:

template< typename C >
inline auto overloaded_pred_for( const C&, bool ( *f )( const C& ) ) -> decltype( f )
{
    return f;
}

Пример использования:

std::all_of( vv.begin(), vv.end(), 
             overloaded_pred_for( std::vector< int >(), std::empty ) );
0 голосов
/ 28 мая 2018

Проблема в том, что нет такого понятия, как std :: empty, насколько я знаю, это функция-член.Попробуйте вместо этого использовать [](const auto& i){ return i.empty(); }.

РЕДАКТИРОВАТЬ: хорошо, я раньше не видел std :: empty, но, как отметил ниже комментатор, он существует, но все же вы можете использовать лямбду

0 голосов
/ 28 мая 2018

Какой самый простой способ обойти это?

Может быть

std::all_of(std::begin(vv), std::end(vv),
            [](auto const & v){ return v.empty(); });

?

0 голосов
/ 28 мая 2018

быстрый обходной путь

#include <algorithm>
#include <vector>

int main() {
   std::vector<std::vector<int>> vv;

   std::all_of(std::begin(vv), std::end(vv), 
    [](const auto &v) {return std::empty(v);});
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...