LINUX.ORG.RU

C++, std::function, шаблоны, что я делаю не так?

 ,


0

3
#include <functional>
#include <algorithm>
#include <vector>

template<typename Iterator>
void test(Iterator begin, Iterator end, std::function<void (Iterator, Iterator)> func)
{
}

template<typename Iterator>
void mysort(Iterator begin, Iterator end)
{
}

int main()
{
        std::vector<int> data;
        test(data.begin(), data.end(), std::sort<std::vector<int>::iterator>);
        test(data.begin(), data.end(), mysort<std::vector<int>::iterator>);
        return 0;
}

Первый вызов функции test компилируется, второй — нет, сообщение об ошибке:

test.cpp: В функции «int main()»:
test.cpp:19:67: ошибка: нет соответствующей функции для вызова «test(std::vector<int>::iterator, std::vector<int>::iterator, <unresolved overloaded function type>)»
  test(data.begin(), data.end(), mysort<std::vector<int>::iterator>);
                                                                   ^
test.cpp:19:67: замечание: candidate is:
test.cpp:6:6: замечание: template<class Iterator> void test(Iterator, Iterator, std::function<void(Iterator, Iterator)>)
 void test(Iterator begin, Iterator end, std::function<void (Iterator, Iterator)> func)
      ^
test.cpp:6:6: замечание:   template argument deduction/substitution failed:
test.cpp:19:67: замечание:   mismatched types «std::function<void(Iterator, Iterator)>» and «void (*)(__gnu_cxx::__normal_iterator<int*, std::vector<int> >, __gnu_cxx::__normal_iterator<int*, std::vector<int> >)»
  test(data.begin(), data.end(), mysort<std::vector<int>::iterator>);
                                                                   ^
test.cpp:19:67: замечание:   could not resolve address from overloaded function «mysort<std::vector<int>::iterator>»
test.cpp: In instantiation of «void test(Iterator, Iterator, std::function<void(Iterator, Iterator)>) [with Iterator = __gnu_cxx::__normal_iterator<int*, std::vector<int> >]»:
test.cpp:18:70:   required from here
test.cpp:6:6: предупреждение: параметр «begin» не используется [-Wunused-parameter]
 void test(Iterator begin, Iterator end, std::function<void (Iterator, Iterator)> func)
      ^
std::sort объявлен так:
  template<typename _RandomAccessIterator>
    inline void
    sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
Кроме inline, нет никаких отличий от mysort, но дело не в inline (пробовал и его добавлять к mysort).

Заставить код компилироваться можно, например, так:

test<std::vector<int>::iterator>(data.begin(), data.end(), mysort<std::vector<int>::iterator>);

Но почему в случае std::sort происходит разворачивание шаблона нормально, а в случае mysort нет?

gcc 4.8.2

В 4.7.2 и clang 3.2 одинаково не работает с std::sort и mysort, одинаково чинится явным вызовом конструктора std::function.

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

BlackHawk ()
Ответ на: комментарий от BlackHawk

inline для шаблонных функций — лишний модификатор, они всегда inline

4.2

anonymous ()
Ответ на: комментарий от BlackHawk

В 4.7.2 и clang 3.2 одинаково не работает с std::sort и mysort, одинаково чинится явным вызовом конструктора std::function.

Явный вызов конструктора std::function работает и у меня, забыл об этом написать. Просто в данном случае короче написать явные параметры шаблона для test, чем конструктор std::function с его параметрами шаблона.

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

Они не всегда inline, более того, я ещё пробовал добавлять inline к mysort, это не помогало, и ещё я пробовал функцию std::inplace_merge, которая без inline, с ней работало, а с моим аналогом точно так же не работало. Я не вижу отличий между своими функциями и функциями из stl, но почему-то мои не разворачиваются, а stl'ные разворачиваются.

gentoo_root ★★★★★ ()
test<std::vector<int>::iterator>(data.begin(), data.end(), mysort<std::vector<int>::iterator>);

Ох г-пди, как всё у крестоносцев запущено. Мыши плакали, кололись, но продолжали грызть кактус. XXI век на дворе, чо.

mix_mix ★★★★★ ()
Ответ на: комментарий от gentoo_root

Есть подозрение, что, пока нет нормального вывода типов, для функций stl написали специальные костыли, чтобы хоть с ними работало

BlackHawk ()
Ответ на: комментарий от BlackHawk

Есть подозрение, что, пока нет нормального вывода типов, для функций stl написали специальные костыли, чтобы хоть с ними работало

Ну пока что это единственное объяснение, и это действительно довольно костыльно.

gentoo_root ★★★★★ ()

патамушта mysort и std::function - это две большие разницы

anonymous ()
Ответ на: комментарий от anonymous

патамушта mysort и std::function - это две большие разницы

Ну и чем же так mysort принципиально отличается от std::sort, что указатель на функцию std::sort автоматически приводится к std::function, а указатель на mysort — нет?

gentoo_root ★★★★★ ()

Почему не просто

template<typename Iterator, typename F>
void test(Iterator begin, Iterator end, F func)

?

forCe ()
Ответ на: комментарий от gentoo_root

потому что mysort - функция, а std::function - класс. Потому что, вероятно, std::sort - не функция, а класс с переопределённым оператором (). Исходники stl, извините, не изучал, но что ТС попросил в качестве параметра можно посмотеть здесь, а что передал - в начале треда.

anonymous ()
Ответ на: комментарий от anonymous

либо есть специализация для каждого из встроенных алгоритмов

anonymous ()

В clang не компилируется ни одна. Ругается на template argument deduction. Прикольно что:

auto f = mysort<std::vector<int>::iterator>
test(data.begin(), data.end(), f);

не компилится, но

std::function<void(std::vector<int>::iterator,std::vector<int>::iterator)> f = mysort<std::vector<int>::iterator>;
test(data.begin(), data.end(),f);

идет на ура.

ebantrop ()
Ответ на: комментарий от ebantrop

так и должно быть, потому что во втором случае передаётся std::function, как и было попрошено, а в первом - mysort, который не имеет никакого отношения к std::function.

anonymous ()
Ответ на: комментарий от anonymous

Потому что, вероятно, std::sort - не функция, а класс с переопределённым оператором ().

Нет, это не функтор, это обычная шаблонная функция, я же в ОП написал, как она объявлена на моём компьютере. Объявление mysort ничем не отличается.

gentoo_root ★★★★★ ()
Ответ на: комментарий от forCe

Потому что непрозрачно (непонятно, что за F, какие типы параметров у него должны быть), а ещё в качестве F теоретически может выступать какая-то левая функция, допустим, void (A, B), при этом есть неявное преобразование из Iterator в A и из Iterator в B, и такая функция прокатит тут, а с std::function — нет.

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

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

anonymous ()
Ответ на: комментарий от gentoo_root

Это возможно и имело бы смысл, если сообщения об ошибках несоответствия были бы более понятными. Но в данном случае ты не получаешь ничего, кроме потенциального оверхеда на std::function, например, в случае лямбд. С другой стороны, ты лишаешь себя возможности передавать туда объекты, с шаблонным operator(). В общем, функция у тебя итак шаблонная, потому не вижу никакого смысла ставить туда std::function. В стандартной библиотеке все функторы передаются просто шаблонным параметром и это правильно. Да и код смотрится несколько проще.

forCe ()
Ответ на: комментарий от anonymous

я тоже написал, что вероятно std::function имеет специализации для встроенных алгоритмов, которые используются для их приведения к std::function.

Если это так, судя по всему, это сделано костылём в компиляторе, потому что я пробовал грепать, например, inplace_merge по /usr/include/c++, и нашёл только саму функцию и форвард для неё, а с ней была ситуация, аналогичная std::sort и mysort.

Используй второй вариант от ебантропа и будет тебе счастье

Не, это слишком длинно, да и решение для себя я нашёл и написал его в ОП, просто хотел разобраться, почему так происходит.

gentoo_root ★★★★★ ()
Ответ на: комментарий от forCe

С другой стороны, ты лишаешь себя возможности передавать туда объекты, с шаблонным operator().

А есть какой-то смысл делать шаблонный operator(), а не сам класс?

Да и код смотрится несколько проще.

Это постоянная беда C++, в котором постоянно приходится писать всякие длинные строки для простых вещей.

gentoo_root ★★★★★ ()
Ответ на: комментарий от gentoo_root

А есть какой-то смысл делать шаблонный operator(), а не сам класс?

Бывает и такое. Кстати, в новом стандарте(С++14) будут «полиморфные лямбды», с auto для параметров. Считай, что тот же самый кейс.

Это постоянная беда C++, в котором постоянно приходится писать всякие длинные строки для простых вещей.

В других языках ты такие «простые» вещи не пишешь вообще. Тебе кто-то мешает взять другой язык?=)

forCe ()
Ответ на: комментарий от gentoo_root

Кстати

template<typename Iterator, typename F>
void test(Iterator begin, Iterator end, F func)
{
}

struct mysort
{
    template<typename Iterator>
    void operator()(Iterator begin, Iterator end)
    {
    }
};

И тогда можно будет писать просто:

test(data.begin(), data.end(), mysort());

forCe ()
Ответ на: комментарий от anonymous

я тоже написал, что вероятно std::function имеет специализации для встроенных алгоритмов

все гораздо проще - std::sort имеет два варианта из которых gcc выбрал «лучший» по кол-ву параметров (первое на что в таком случае смотрит компилятор) , а во втором случае он протупил, ИМХО баг gcc

wota ★★ ()
Ответ на: комментарий от wota

При выведении типов в C++ учитываются неявные преобразования? Если нет, то и первый случай должен вызывать ошибку.

И причем здесь перегрузка функции std::sort? Вроде бы изначально было ясно, что ТС предполагал вариант с двумя аргументами.

anonymous ()
Ответ на: комментарий от anonymous

внезапно:

~$ cat 1.cpp
#include <functional>
#include <algorithm>
#include <vector>

template<typename Iterator>
void test(Iterator begin, Iterator end, std::function<void (Iterator, Iterator)> func)
{
}

template<typename Iterator>
void mysort(Iterator begin, Iterator end)
{
}

template<typename Iterator, typename Compare>
void mysort(Iterator begin, Iterator end, Compare comp)
{
}


int main()
{
        std::vector<int> data;
        test(data.begin(), data.end(), std::sort<std::vector<int>::iterator>);
        test(data.begin(), data.end(), mysort<std::vector<int>::iterator>);
        return 0;
}
~$ g++ -std=c++11 ./1.cpp
~$ 
wota ★★ ()
Ответ на: комментарий от wota

все гораздо проще - std::sort имеет два варианта из которых gcc выбрал «лучший» по кол-ву параметров (первое на что в таком случае смотрит компилятор) , а во втором случае он протупил

Действительно, если сделать два mysort, то тоже компилируется, теперь я понял, чем отличается мой mysort от std::sort.

gentoo_root ★★★★★ ()
Ответ на: комментарий от wota

Т.е. если кроме подходящей сигнатуры есть и неподходящая, то gcc подходящую находит, а если неподходящей нет, то и подходящую не находит? Да, согласен, — это баг gcc.

anonymous ()
Ответ на: комментарий от anonymous

Под «подходящая» следует понимать, что можно нейявно приобразовать к требуемому типу.

Но, ЕМНИП, при выводе типов неявные преобразования не учитываются. Получается, что баг здесь в том, что при наличии дополнительных определений ошибка пропадает.

anonymous ()
Ответ на: комментарий от BlackHawk

inline для шаблонных функций — лишний модификатор, они всегда inline

Нет, хотя у них есть общее свойство - они компилируются в weak символы, поэтому на них не действует one definition rule.

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

annulen ★★★★★ ()

что я делаю не так?

трогаешь C++

lazyklimm ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.