LINUX.ORG.RU

[c++0x]Почему в qsort можно передать незамкнутую лямбду, но нельзя замкнутую?


0

1
int main()
{
  int a[10] = {0,0,0,0,0,0,0,0,0,0};
  int order = 1;
  std::qsort(a,10,sizeof(int),[](const void *p1,const void *p2) -> int
                         {
                           int order = 1;
                           if(order==1)
                             return *(int *)p1-*(int*)p2;
                           if(order==2)
                             return *(int *)p2-*(int*)p1;
                         });
  return 0;
}

компилируется

#include <cstdlib>

int main()
{
  int a[10] = {0,0,0,0,0,0,0,0,0,0};
  int order = 1;
  std::qsort(a,10,sizeof(int),[order](const void *p1,const void *p2) -> int
                         {
                           if(order==1)
                             return *(int *)p1-*(int*)p2;
                           if(order==2)
                             return *(int *)p2-*(int*)p1;
                         });
  return 0;
}

не компилируется

lambda.cpp:13:6: ошибка: cannot convert ‘main()::<lambda(const void*, const void*)>’ to ‘int (*)(const void*, const void*)’ for argument ‘4’ to ‘void qsort(void*, size_t, size_t, int (*)(const void*, const void*))’
★★★

Последнее исправление: Yareg (всего исправлений: 1)

Кстати, если замыкать order по ссылке, т.е. [&order], то ничего не меняется.

Yareg ★★★
() автор топика

вероятно потому-что order - это также параметр, и когда компилятор для себя преобразовывает лямбду в функцию или функтор - он его «дописывает»

anonymous
()

std::sort же, а не С-ный qsort. Передавать лямбду в С-функцию - моветон.

JackYF ★★★★
()

а как вообще тогда без говнотемплейтов передавать замкнутую лямбду? каждая подстановка темплейта же делает сотни кода...

и ведь вполне очевидно, как реализовать это так, чтобы работало... надо, кстати посмотреть, как оно в асме выглядит.

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

>и ведь вполне очевидно, как реализовать это так, чтобы работало...

Да ну? Расскажи-ка, куда в void* ты засунешь захваченные данные. Не используй С-функции, и дело с концом. Неужели ты думаешь, что разработчики GCC глупее нас с тобой? :)

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

> Захваченные данные можно хранить в статической памяти

Нет, не будет работать. Представь, что у тебя есть объект, который содержит ссылки на самого себя. Единственный способ его правильно скопировать - вызывать конструктор копирования, чего компилятор делать не имеет права. Более того, если объект захвачен по ссылке, кто будет обновлять статическую память, когда основной объект будет изменяться?

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

>Нет, не будет работать. Представь, что у тебя есть объект, который содержит ссылки на самого себя. Единственный способ его правильно скопировать - вызывать конструктор копирования, чего компилятор делать не имеет права. Более того, если объект захвачен по ссылке, кто будет обновлять статическую память, когда основной объект будет изменяться?

Хотя нет, тут я не прав. Компилятор таки имеет право копировать объект, и ссылку обновлять не надо.

Но всё-таки мне кажется, что это работать в общем случае не будет. А вообще - можешь действительно разработчиков спросить, вдруг ответят.

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

>Захваченные данные можно хранить в статической памяти

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

anonymous
()

блин... он ассемблирует

#include <iostream>

using namespace std;

template <typename T>
void ff(T func)
{
  int a=1;
  int b=2;
  cout<<func(&a,&b)<<"\n";
}

int main()
{
  int a[10] = {0,0,0,0,0,0,0,0,0,0};
  int order = 1;
  auto f = [order](const void *p1,const void *p2) -> int
           {
             if(order==1)
               return *(int *)p1-*(int*)p2;
             if(order==2)
               return *(int *)p2-*(int*)p1;
           };
  auto f2 = [&order](const void *p1,const void *p2) -> int
            {
              if(order==1)
                return *(int *)p1-*(int*)p2;
              if(order==2)
                return *(int *)p2-*(int*)p1;
            };
  ff(f);
  ff(f2);
  order=2;
  ff(f);
  ff(f2);
  return 0;
}

_в точности_ как

#include <iostream>

using namespace std;

int f(int *order,const void *p1,const void *p2)
{
if(*order==1)
  return *(int *)p1-*(int*)p2;
if(*order==2)
  return *(int *)p2-*(int*)p1;  
}

int f2(int **order,const void *p1,const void *p2)
{
if(**order==1)
  return *(int *)p1-*(int*)p2;
if(**order==2)
  return *(int *)p2-*(int*)p1;  
}

void ff(int);

void ff2(int *);

int main()
{
  int a[10] = {0,0,0,0,0,0,0,0,0,0};
  int order = 1;
  int order_copy = order;
  int *porder = &order;
  ff(order_copy);
  ff2(porder);
  order=2;
  ff(order_copy);
  ff2(porder);
  return 0;
}

void ff(int order)
{
  int a=1;
  int b=2;
  cout<<f(&order,&a,&b)<<"\n";
}

void ff2(int *order)
{
  int a=1;
  int b=2;
  cout<<f2(&order,&a,&b)<<"\n";
}

в диффе отличается только расположение переменных на стеке и идентификаторы.

уныло как-то

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

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

Те языки, я думаю просто не определяют всё это на этапе компиляции. В C++ так не выйдет.

Yareg ★★★
() автор топика

Однако я нашёл одну особенность — константные лямбды и развёрнутые шаблоны на -03 (а может и меньше) тупо вырезаются, в то время как явно объявленные функции остаются в коде, хоть и ни разу не вызываются. Странно как-то...

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

>А вообще - можешь действительно разработчиков спросить, вдруг ответят.

Разработчиков GCC? Мне почему-то кажется, что такое поведение описано в черновике стандарта...

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

В оф.доках написано, что лямбда-конструкция создаёт _класс_ с перегруженным operator() и конструктором, в который передаются захваченные переменные.

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

> Захваченные данные можно хранить в статической памяти

предположим, что

1. qsort полученный ею адрес анонимной функциие запишет в некую глобальную переменную Х и далее вызывает эту анонимную функцию

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

и тогда твоя статическая память перепишется ими

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

>вызовет ту самую анонимную функцию, но уже с другими захваченными данными

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

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

> данные один раз захватились в том месте кода, где у нас квадратные скобки и больше не меняются.

так скорее всего быть не может

короче, напиши сишный код для МОЕГО сценария, где анонимная функция вызывается 2 раза, тогда будет что-то ясно

www_linux_org_ru ★★★★★
()

твоя идея возможно прошла бы, если бы у std::sort была аннотация типа (сделанная программистом или выведенная компилятором) вида @consume(comparator)

www_linux_org_ru ★★★★★
()

вот че похоже будет иметь проблемы со статической памятью:

typedef int (*func)(int);

void driver(int i);

int sort(int i, func f) 
{
  return i<10000 ? f(i)+driver(i+1) : 0;
}

int driver(int i)
{
  int order=i;
  return sort( i, [order](int j) -> int { return order*j*j; } );  
}

int main()
{
  driver(1);
  return 0;
}
www_linux_org_ru ★★★★★
()

или даже так:

typedef int (*func)(int);

int driver(int i);

int sort(int i, func f) 
{
  if( i<10000 ) {
    int temp = driver(i+1);
    return f(i)+temp;
  }else{
    return 0;
  }
}

int driver(int i)
{
  int order=i;
  return sort( i, [order](int j) -> int { return order*j*j; } );  
}

int main()
{
  driver(1);
  return 0;
}

тут как минимум потребуется 10000 слов статической памяти под order, не? во всяком случае привязка «сразу» не прокатит

хотя может ты и придумаешь как это сделать

www_linux_org_ru ★★★★★
()

std::qsort ??? что это вообще такое ?

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

в диффе отличается только расположение переменных на стеке и идентификаторы.

уныло как-то

А ты хотел на выходе получить какой-то волшебный байткод что-ли?

Deleted
()
Ответ на: комментарий от www_linux_org_ru
typedef int (*func)(int);  
  
int driver(int i);  
  
int sort(int i, func f)   
{  
  if( i<10000 ) {  
    int temp = driver(i+1);  
    return f(i)+temp;  
  }else{  
    return 0;  
  }  
}  
  
int driver(int i)  
{  
  int order=i;  
  return sort( i, [order](int j) -> int { return order*j*j; } );    
}  
  
int main()  
{  
  driver(1);  
  return 0;  
} 

сейчас компилируется, как

typedef int (*func)(int);  
  
int driver(int i);  
  
int sort(int i, func f)   
{  
  if( i<10000 ) {  
    int temp = driver(i+1);  
    return f(i)+temp;  
  }else{  
    return 0;  
  }  
}  
  
class anonymous000 
{ 
  int order; 
public: 
  anonymous000(int _order); 
  int operator()(int j); 
} 
anonymous000::anonymous000(int _order) : order(_order) {}; 
anonymous000::anonymous000(int j) { return order*j*j; } 
 
int driver(int i)  
{  
  int order=i;  
  return sort( i, new anonymous000(order) ); 
}  
  
int main()  
{  
  driver(1);  
  return 0;  
}

и естественно не скомпилируется

но это, наверное, более C++-way, чем то, что я думаю

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

а я думал, что

typedef int (*func)(int);  
  
int driver(int i);  
  
int sort(int i, func f)   
{  
  if( i<10000 ) {  
    int temp = driver(i+1);  
    return f(i)+temp;  
  }else{  
    return 0;  
  }  
} 
 
const int __order; 
int anonymous000(int j) 
{  return __order*j*j; } 
  
int driver(int i)  
{  
  int order=i;  
  const_cast<int>__order=order; //замкнули 
  return sort( i, anonymous000 );    
}  
  
int main()  
{  
  driver(1);  
  return 0;  
} 

в общем, всё это пофигу, не надо использовать лямбды в сишных функциях

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

вообще надо бы адаптировать под лямбды С calling сonvention... если это возможно

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

> в то время как явно объявленные функции остаются в коде, хоть и ни разу не вызываются.

Либо поместите их в анонимный namespace, либо используйте static, либо __attribute__((vsisibility(«hidden»))), тогда оптимизатор будет вырезать неиспользуемый код.

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

> каждая подстановка темплейта же делает сотни кода...

А у тебя что, винт на 20 мегабайт?

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