2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему
 
 Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 20:42 


11/08/18
363
Добрый день,

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

код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <stdio.h>

#include <functional>

template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, int (*CMP)(T &, T &)) // сортирует так, чтобы получилось [0]>=[1]>=...,>=[N-1]
{ int i, j1, j2, k;
  T *pr, *p1, *p2;

  if(ResPlace)
  { if(N==2)
    { if(CMP(p[1],p[0])) { buf[0]=p[1]; buf[1]=p[0]; }
      else { buf[0]=p[0]; buf[1]=p[1]; }
      return;
    } else if(N==1)
    { buf[0]=p[0];
      return;
    } else
    { k=N/2;
      SortFunc(0, k, p, buf, CMP);
      SortFunc(0, N-k, p+k, buf+k, CMP);
      pr=buf;
      p1=p;
      p2=p+k;
    }
  } else
  { if(N==2)
    { if(CMP(p[1],p[0])) { buf[0]=p[0]; p[0]=p[1]; p[1]=buf[0]; }
      return;
    } else if(N==1) return;
    else
    { k=N/2;
      SortFunc(1, k, p, buf, CMP);
      SortFunc(1, N-k, p+k, buf+k, CMP);
      pr=p;
      p1=buf;
      p2=buf+k;
    }
  }
  j1=j2=i=0;
  while(1)
  { if(CMP(p1[j1],p2[j2]))
    { pr[i++]=p1[j1++];
      if(j1==k) { while(i<N) pr[i++]=p2[j2++]; return; }
    } else
    { pr[i++]=p2[j2++];
      if(j2==N-k)
      { while(i<N) pr[i++]=p1[j1++]; return; }
    }
  }
}


int FuncM(int &t1, int &t2) { return (t1<t2)?1:0; }
int FuncG(int &t1, int &t2) { return (t1>t2)?1:0; }


template <typename T> inline void SortLow(int N, T *Data, T *Buf) // сортирует так, чтобы получилось [0]<=[1]<=...,<=[N-1]
{ auto Func1 { [] (T &t1, T &t2) -> int { return (t1>t2)?1:0; }};
  std::function<int(T&,T&)> Func2 = [&] (T &t1, T &t2) -> int { return (t1>t2)?1:0; };
  SortFunc(0, N, Data, Buf, FuncG); // здесь все работает
//  SortFunc(0, N, Data, Buf, &Func1); // но хочется, чтобы работало с ламбдой, так как не хочется описывать функцию глобально
//  SortFunc(0, N, Data, Buf, Func2); // во всех остальных вариациях тоже не получается...
//  SortFunc(0, N, Data, Buf, &Func1); // и здесь
//  SortFunc(0, N, Data, Buf, &Func2); // и здесь
//  SortFunc(0, N, Data, Buf, [&] (T &t1, T &t2) { return (t1>t2)?1:0; }); // и даже здесь
}

template <typename T> inline void SortHigh(int N, T *Data, T *Buf) // сортирует так, чтобы получилось [0]>=[1]>=...,>=[N-1]
{ SortFunc(0, N, Data, Buf, FuncM); // здесь все работает
}

int main()
{ int a[] = {1, 2, 5, 8, 8, 3, 11};
  int Buf[7];

  SortLow(sizeof(a)/sizeof(a[0]), a, Buf);
  for(int i=0; i<7; i++) printf("%d ", a[i]);
  printf("\n");
  SortHigh(sizeof(a)/sizeof(a[0]), a, Buf);
  for(int i=0; i<7; i++) printf("%d ", a[i]);
  printf("\n");
  return 0;
}
 


Помогите, пожалуйста, советом, что я делаю не так, и как подставить лямбду в темплейт выше.

Спасибо!

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 20:50 
Заслуженный участник
Аватара пользователя


06/10/08
6422
Используется синтаксис C++
 template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, std::function<int(T&, T&)> CMP)

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 21:43 


11/08/18
363
Спасибо большое, Xaositect !

Xaositect в сообщении #1501757 писал(а):
Используется синтаксис C++
 template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, std::function<int(T&, T&)> CMP)


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

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 21:45 
Заслуженный участник
Аватара пользователя


06/10/08
6422
ilghiz в сообщении #1501766 писал(а):
Да, до такого я не додумался, ибо хотелось и обычно описанные функции туда же подставлять, а так их всех через std::function объявлять придется.
Нет, можно вызывать и с обычной функцией, там преобразование пройдет (у std::function есть нужный конструктор).

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 21:55 


11/08/18
363
Xaositect в сообщении #1501767 писал(а):
Нет, можно вызывать и с обычной функцией, там преобразование пройдет (у std::function есть нужный конструктор).


Спасибо за ответ! У меня правда не работает:

код: [ скачать ] [ спрятать ]
Используется синтаксис C++
#include <stdio.h>
#include <functional>

template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, std::function<int(T&, T&)> CMP)
{ int i, j1, j2, k;
  T *pr, *p1, *p2;

  if(ResPlace)
  { if(N==2)
    { if(CMP(p[1],p[0])) { buf[0]=p[1]; buf[1]=p[0]; }
      else { buf[0]=p[0]; buf[1]=p[1]; }
      return;
    } else if(N==1)
    { buf[0]=p[0];
      return;
    } else
    { k=N/2;
      SortFunc(0, k, p, buf, CMP);
      SortFunc(0, N-k, p+k, buf+k, CMP);
      pr=buf;
      p1=p;
      p2=p+k;
    }
  } else
  { if(N==2)
    { if(CMP(p[1],p[0])) { buf[0]=p[0]; p[0]=p[1]; p[1]=buf[0]; }
      return;
    } else if(N==1) return;
    else
    { k=N/2;
      SortFunc(1, k, p, buf, CMP);
      SortFunc(1, N-k, p+k, buf+k, CMP);
      pr=p;
      p1=buf;
      p2=buf+k;
    }
  }
  j1=j2=i=0;
  while(1)
  { if(CMP(p1[j1],p2[j2]))
    { pr[i++]=p1[j1++];
      if(j1==k) { while(i<N) pr[i++]=p2[j2++]; return; }
    } else
    { pr[i++]=p2[j2++];
      if(j2==N-k)
      { while(i<N) pr[i++]=p1[j1++]; return; }
    }
  }
}


int FuncM(int &t1, int &t2) { return (t1<t2)?1:0; }
int FuncG(int &t1, int &t2) { return (t1>t2)?1:0; }


template <typename T> inline void SortLow(int N, T *Data, T *Buf) // сортирует так, чтобы получилось [0]<=[1]<=...,<=[N-1]
{ //  auto Func1 { [] (T &t1, T &t2) -> int { return (t1>t2)?1:0; }};
  std::function<int(T&,T&)> Func = [&] (T &t1, T &t2) -> int { return (t1>t2)?1:0; };
//  SortFunc(0, N, Data, Buf, &Func1);
//  SortFunc(0, N, Data, Buf, Func2);
//  SortFunc(0, N, Data, Buf, &Func1);
  SortFunc(0, N, Data, Buf, Func);
//  SortFunc(0, N, Data, Buf, [&] (T &t1, T &t2) { return (t1>t2)?1:0; });
  SortFunc(0, N, Data, Buf, FuncG);
}

template <typename T> inline void SortHigh(int N, T *Data, T *Buf) // сортирует так, чтобы получилось [0]>=[1]>=...,>=[N-1]
{ // auto Func = [&] (T &t1, T &t2) -> int { return (t1>t2)?1:0; };
  std::function<int(T&,T&)> Func = [&] (T &t1, T &t2) -> int { return (t1<t2)?1:0; };
  SortFunc(0, N, Data, Buf, Func);
  SortFunc(0, N, Data, Buf, FuncM);
}

int main()
{ int a[] = {1, 2, 5, 8, 8, 3, 11};
  int Buf[7];

  SortLow(sizeof(a)/sizeof(a[0]), a, Buf);
  for(int i=0; i<7; i++) printf("%d ", a[i]);
  printf("\n");
  SortHigh(sizeof(a)/sizeof(a[0]), a, Buf);
  for(int i=0; i<7; i++) printf("%d ", a[i]);
  printf("\n");
  return 0;
}
 


вылетая на компиляции:

Код:
$ g++-10 -std=c++17 -Wall -O3 sort.cpp
sort.cpp: In instantiation of ‘void SortLow(int, T*, T*) [with T = int]’:
sort.cpp:88:41:   required from here
sort.cpp:74:11: error: no matching function for call to ‘SortFunc(int, int&, int*&, int*&, int (&)(int&, int&))’
   74 |   SortFunc(0, N, Data, Buf, FuncG);
      |   ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
sort.cpp:14:35: note: candidate: ‘template<class T> void SortFunc(int, int, T*, T*, std::function<int(T&, T&)>)’
   14 | template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, std::function<int(T&, T&)> CMP)
      |                                   ^~~~~~~~
sort.cpp:14:35: note:   template argument deduction/substitution failed:
sort.cpp:74:11: note:   mismatched types ‘std::function<int(T&, T&)>’ and ‘int (*)(int&, int&)’
   74 |   SortFunc(0, N, Data, Buf, FuncG);
      |   ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
sort.cpp: In instantiation of ‘void SortHigh(int, T*, T*) [with T = int]’:
sort.cpp:91:42:   required from here
sort.cpp:81:11: error: no matching function for call to ‘SortFunc(int, int&, int*&, int*&, int (&)(int&, int&))’
   81 |   SortFunc(0, N, Data, Buf, FuncM);
      |   ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
sort.cpp:14:35: note: candidate: ‘template<class T> void SortFunc(int, int, T*, T*, std::function<int(T&, T&)>)’
   14 | template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, std::function<int(T&, T&)> CMP)
      |                                   ^~~~~~~~
sort.cpp:14:35: note:   template argument deduction/substitution failed:
sort.cpp:81:11: note:   mismatched types ‘std::function<int(T&, T&)>’ and ‘int (*)(int&, int&)’
   81 |   SortFunc(0, N, Data, Buf, FuncM);
      |   ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~


Код:
$ g++-10 -v
Using built-in specs.
COLLECT_GCC=g++-10
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/10/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 10.2.0-5ubuntu1~20.04' --with-bugurl=file:///usr/share/doc/gcc-10/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-10 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-10-WJNXnb/gcc-10-10.2.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-10-WJNXnb/gcc-10-10.2.0/debian/tmp-gcn/usr,hsa --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.0 (Ubuntu 10.2.0-5ubuntu1~20.04)


на g++-9 - то же самое :(

Скажите, пожалуйста, что я делаю не так?

Спасибо!

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 22:04 
Заслуженный участник
Аватара пользователя


06/10/08
6422
Можно явно указать параметр SortFunc<int>(0, N, Data, Buf, FuncG).
Позже посмотрю подробнее, может быть, что-то можно сделать, чтобы выводился.

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 22:07 


11/08/18
363
Ой, а если явно преобразовывать, то да, все начинает работать:

Используется синтаксис C++
SortFunc(0, N, Data, Buf, (std::function<int(T&,T&)>)FuncM);
 


Спасибо большое!!!

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 22:09 
Заслуженный участник
Аватара пользователя


06/10/08
6422
В принципе можно еще забыть про std::function и сделать
Используется синтаксис C++
template <typename T, typename F> inline void SortFunc(int ResPlace, int N, T *p, T *buf, F CMP)

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 22:27 


11/08/18
363
Xaositect в сообщении #1501776 писал(а):
В принципе можно еще забыть про std::function и сделать
Используется синтаксис C++
template <typename T, typename F> inline void SortFunc(int ResPlace, int N, T *p, T *buf, F CMP)

классно, спасибо!!! Это вроде работает и нет нужды в std::function, которую компилер может и не додуматься оптимизировать...

Спасибо большое!!!

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение23.02.2021, 14:36 
Аватара пользователя


07/02/12
1433
Питер
ilghiz в сообщении #1501778 писал(а):
классно, спасибо!!! Это вроде работает и нет нужды
Вы учтите только, что эта функция будет скопирована TxF раз (для всех комбинаций T и компаратора), разворачивая и сам компаратор, безотносительно - указали вы inline или нет. Что при глубоких вложенностях может быть довольно громоздко.

По существу - т.к. лямбда у вас темплейтная, и <SortFunc> темплейтный, компилятор не знает что делать.
Лечится это явным приведением лямбды к типу функции, должно заработать:
Используется синтаксис C++
int (*Func1)(T&, T&) = [](T& t1, T&t2) -> int { return (t1>t2)?1:0; };
SortFunc(0, N, Data, Buf, Func1);
 
или, соответственно,
Используется синтаксис C++
SortFunc(0, N, Data, Buf, (int (*)(T&, T&)) [](T& t1, T&t2) -> int { return (t1>t2)?1:0; });
 

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

 Профиль  
                  
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение28.10.2021, 21:26 
Аватара пользователя


28/10/21
100
ilghiz в сообщении #1501754 писал(а):
Помогите, пожалуйста, советом, что я делаю не так, и как подставить лямбду в темплейт выше.


Вот этот вариант вызова

Используется синтаксис C++
template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf, int (*CMP)(T &, T &))
...
auto Func1 { [] (T &t1, T &t2) -> int { return (t1>t2)?1:0; }};
SortFunc(0, N, Data, Buf, &Func1);


вполне жизнеспособен. Ваша шаблонная функция сортировки `SortFunc` последним параметром принимает указатель на обычную функцию, а вы пытаетесь передавать туда лямбду (точнее, указатель на лямбду). Лямбда без захвата (пустые `[]`) в С++ является неявно приводимой к указателю на обычную функцию, но для этого, разумеется, передавать нужно саму лямбду `Func1`, а не `&Func1`. Откуда у вас возникла странная идея передавать `&Func1` - не ясно.

Исправляем

Используется синтаксис C++
SortFunc(0, N, Data, Buf, Func1);


Уже лучше, но этот вызов по-прежнему не скомпилируется потому, что правила дедукции шаблонных аргументов в С++ требуют, чтобы все дедукции шаблонных аргументов из фактических аргументов функции в вызове были успешны и давали одинаковые результаты. В этом вызове компилятор будет пытаться дедуцировать шаблонный аргумент `T` как из аргументов `Data` и `Buf`, так и из аргумента `Func1`. Дедукция из аргументов `Data` и `Buf` пройдет успешно, а вот из `Func1` он дедуцировать ничего не сможет. Это и приведет к ошибке компиляции.

Простейший способ исправить это - указать шаблонный аргумент в вызове явно, то есть полностью отключить дедукцию вообще

Используется синтаксис C++
SortFunc<T>(0, N, Data, Buf, Func1);


И этот вызов сразу же начнет компилироваться и работать без проблем!

Однако явное указание шаблонного аргумента - это не совсем удобно и не совсем элегантно. Альтернативное решение: попросить компилятор вообще не использовать последний аргумент функции `SortFunc` для дедукции шаблонного аргумента `T`. Пусть компилятор пользуется для этого только третьим и четвертым аргументом - этого ему будет достаточно.

Как этого достичь? Для этого мы должны преднамеренно поместить последний параметр `SortFunc` в т.наз. недедуцируемый контекст (non-deduced context). Это делается при помощи следующего идиоматического класса-помощника

Используется синтаксис C++
template <typename T> struct NonDeduced { using type = T; };


и последующего исправления объявления шаблона функции `SortFunc` на

Используется синтаксис C++
template <typename T> inline void SortFunc(int ResPlace, int N, T *p, T *buf,
                                           typename NonDeduced<int (*)(T &, T &)>::type CMP)


Готово. Теперь вызов

Используется синтаксис C++
SortFunc(0, N, Data, Buf, Func1);


будет работать прекрасно.

Остальные ваши вызовы спасти таким способом нельзя, так как в них вы используете лямбды с захватом (с `[&]`) или `std::function`. Ни лямбда с захватом, ни `std::function` не могут быть приведены к обычному указателю на функцию.

---

Начиная с С++20 вам не нужно самостоятельно писать этот вспомогательный класс `NonDeduced`. Стандартная библиотека предоставляет вам класс `std::type_identity`, который делает то же самое.

---

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

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 11 ] 

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group