2014 dxdy logo

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

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




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

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

код: [ скачать ] [ спрятать ]
Используется синтаксис 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 
Аватара пользователя
Используется синтаксис 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 
Спасибо большое, 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 
Аватара пользователя
ilghiz в сообщении #1501766 писал(а):
Да, до такого я не додумался, ибо хотелось и обычно описанные функции туда же подставлять, а так их всех через std::function объявлять придется.
Нет, можно вызывать и с обычной функцией, там преобразование пройдет (у std::function есть нужный конструктор).

 
 
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 21:55 
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 
Аватара пользователя
Можно явно указать параметр SortFunc<int>(0, N, Data, Buf, FuncG).
Позже посмотрю подробнее, может быть, что-то можно сделать, чтобы выводился.

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

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


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

 
 
 
 Re: Ламбда при вызове шаблона(C++), почему у меня не получается?
Сообщение18.01.2021, 22:09 
Аватара пользователя
В принципе можно еще забыть про 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 
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 
Аватара пользователя
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 
Аватара пользователя
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 ] 


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group