Вечный вопрос.
От: c-smile Канада http://terrainformatica.com
Дата: 19.08.09 18:49
Оценка:
Есть такой код:

#include <iostream>

using namespace std;

class functor
{
  int state;
public:
  functor(int initial):state(initial) {}
  void operator()()  { ++state; printf("state %d\n", state); }
};

void do_for_each( int count, functor& action ) // line 13
{
  while(--count >= 0)
    action();
}

int main()
{
    do_for_each( 3, functor(12) ); // line 21
    return 0;
}


VC компилирует его и исполняет на ура.
GCC отказывается с такими матюками
C:\tests\temp_args\main.cpp||In function `int main()':|
C:\tests\temp_args\main.cpp|21|error: invalid initialization of non-const reference of type 'functor&' from a temporary of type 'functor'|
C:\tests\temp_args\main.cpp|14|error: in passing argument 2 of `void do_for_each(int, functor&)'|


Как этот GCC уговорить чтобы он понял что я ему хочу сказать?
Re: Вечный вопрос.
От: Аноним  
Дата: 19.08.09 18:57
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


Вариант 1.
int main()
{
   functor f(12);
   do_for_each( 3, f ); // line 21
   return 0;
}


Вариант 2.

class functor
{
  int state;
public:
  functor(int initial):state(initial) {}
  void operator()() const { ++state; printf("state %d\n", state); }
};

void do_for_each( int count, const functor& action ) // line 13
{
  while(--count >= 0)
    action();
}
Re: Вечный вопрос.
От: MescalitoPeyot Украина  
Дата: 19.08.09 18:59
Оценка:
Здравствуйте, c-smile, Вы писали:

void do_for_each( int count, functor const & action ) // line 13
{
while(--count >= 0)
action();
}

?
... << RSDN@Home 1.2.0 alpha 4 rev. 1138>>
Re[2]: Вечный вопрос.
От: Аноним  
Дата: 19.08.09 19:02
Оценка:
ну и попробуй скомпилировать вариант 2. надо бы мьютабл не забыть для него.
Re[2]: Вечный вопрос.
От: Lorenzo_LAMAS  
Дата: 19.08.09 19:03
Оценка: +1
Здравствуйте, MescalitoPeyot, Вы писали:

MP>Здравствуйте, c-smile, Вы писали:


MP>void do_for_each( int count, functor const & action ) // line 13

MP>{
MP> while(--count >= 0)
MP> action();
MP>}

MP>?

так же, как и в ответе анонима — пробуем компилировать.
Of course, the code must be complete enough to compile and link.
Re: Вечный вопрос.
От: c-smile Канада http://terrainformatica.com
Дата: 19.08.09 19:23
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал.
Насколько я знаю приведенный код абсолютно легален с точки зрения стандарта. Если нет то также буду признателен если кто-нибудь скажет почему.
Re[2]: Вечный вопрос.
От: VoidEx  
Дата: 19.08.09 19:25
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, c-smile, Вы писали:


CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


CS>Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал.

CS>Насколько я знаю приведенный код абсолютно легален с точки зрения стандарта. Если нет то также буду признателен если кто-нибудь скажет почему.
Временным объектом нельзя инициализировать lvalue ref, жди rvalue ref (&&)
Re[2]: Вечный вопрос.
От: Fwiffo Россия  
Дата: 19.08.09 19:32
Оценка: 36 (1)
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, c-smile, Вы писали:


CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


CS>Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал.

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

8.5.3
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
— If the initializer expression
    — is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
    — has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type “cv3 T3,” where
...
— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or shall
be an rvalue reference.
Re[2]: Вечный вопрос.
От: Andrew S Россия http://alchemy-lab.com
Дата: 19.08.09 19:46
Оценка:
CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?

CS>Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал.

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

Как уже тут сказали — 8.5.3/5
Кроме явного указания, есть еще и другие причины. Подробнее — http://rsdn.ru/forum/cpp/317090.aspx
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[3]: Вечный вопрос.
От: c-smile Канада http://terrainformatica.com
Дата: 19.08.09 19:55
Оценка:
Здравствуйте, Fwiffo, Вы писали:

F>Здравствуйте, c-smile, Вы писали:


CS>>Здравствуйте, c-smile, Вы писали:


CS>>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


CS>>Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал.

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

F>8.5.3

F>
F>A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
F>— If the initializer expression
F>    — is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2,” or
F>    — has a class type (i.e., T2 is a class type) and can be implicitly converted to an lvalue of type “cv3 T3,” where
F>...
F>— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or shall
F>be an rvalue reference.
F>


Спасибо. А какой-нить другой стандарт у вас товарищи есть в котором вот эти два фрагмента

int main()
{
    {
        functor _t(12);
        do_for_each( 3, _t );
    }
    return 0;
}


и

int main()
{
    {
        do_for_each( 3, functor(12) );
    }
    return 0;
}


были бы эквивалентны? Или чтобы не надо было mutable members городить на пустом месте...

Нет хэппи в лайфе, хоть фэйсом об тэйбл.
Re: Вечный вопрос.
От: Alexander G Украина  
Дата: 19.08.09 20:09
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?



int main()
{
    do_for_each( 3, const_cast<functor&>(static_cast<const functor&>(functor(12))) ); // line 21
    return 0;
}


comeau скушал.
gcc скушал

Букву стандарта не ищу, но верю, что она есть.
Русский военный корабль идёт ко дну!
Re: Вечный вопрос.
От: Cyberax Марс  
Дата: 19.08.09 20:11
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?

Как вариант:

#include <iostream>

using namespace std;

class functor
{
  int state;
public:
  functor(int initial):state(initial) {}
  void operator()()  { ++state; printf("state %d\n", state); }
}

void do_for_each( int count, functor* action ) // line 13
{
  while(--count >= 0)
    (*action)();
}

int main()
{
    do_for_each( 3, &functor(12) ); // line 21
    return 0;
}


Но тебе он не нравится
Sapienti sat!
Re: Вечный вопрос.
От: Шахтер Интернет  
Дата: 19.08.09 20:11
Оценка: 110 (7) :))) :))) :)
Здравствуйте, c-smile, Вы писали:


#include <iostream>

using namespace std;

class functor
{
  int state;
public:
  functor(int initial):state(initial) {}
  void operator()()  { ++state; printf("state %d\n", state); }

  functor & operator + () { return *this; }
};

void do_for_each( int count, functor& action ) // line 13
{
  while(--count >= 0)
    action();
}

int main()
{
    do_for_each( 3, +functor(12) ); // line 21
    return 0;
}
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re[2]: Вечный вопрос.
От: Николай Ивченков  
Дата: 19.08.09 20:32
Оценка: +1
Cyberax:

CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?

C>Как вариант:

C>
#include <iostream>

using namespace std;

class functor
{
  int state;
public:
  functor(int initial):state(initial) {}
  void operator()()  { ++state; printf("state %d\n", state); }
}

void do_for_each( int count, functor* action ) // line 13
{
  while(--count >= 0)
    (*action)();
}

int main()
{
    do_for_each( 3, &functor(12) ); // line 21
    return 0;
}


C>Но тебе он не нравится


"The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id." (5.3.1/2)
Re[4]: Вечный вопрос.
От: Fwiffo Россия  
Дата: 19.08.09 20:45
Оценка: 54 (1)
Здравствуйте, c-smile, Вы писали:

CS>Спасибо. А какой-нить другой стандарт у вас товарищи есть в котором вот эти два фрагмента

CS>были бы эквивалентны? Или чтобы не надо было mutable members городить на пустом месте...

CS>Нет хэппи в лайфе, хоть фэйсом об тэйбл.


Можно добавить в functor функцию
functor& self() { return *this; }

и вызывать так:
do_for_each( 3, functor(12).self() );



self возвращает уже lvalue reference, поэтому сработает1.
Re: Вечный вопрос.
От: D14  
Дата: 19.08.09 20:52
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


int main()
{
    do_for_each( 3, functor(12)=functor(12) ); // line 21
    return 0;
}
Re[2]: Вечный вопрос.
От: c-smile Канада http://terrainformatica.com
Дата: 19.08.09 21:52
Оценка: 12 (1) :)
Здравствуйте, D14, Вы писали:

D14>Здравствуйте, c-smile, Вы писали:



CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?


D14>
D14>int main()
D14>{
D14>    do_for_each( 3, functor(12)=functor(12) ); // line 21
D14>    return 0;
D14>}
D14>


И тут я ощутил себя одним из этих зрителей: http://www.youtube.com/watch?v=Nb4shlAyaBo
16.12.06 16:51 : Театр ДГУ - Эммануэль
Re: Вечный вопрос.
От: rg45 СССР  
Дата: 19.08.09 22:51
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>...

CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?

Лично мне наиболее естественным решением кажется вариант с const и mutable (даже странно, что никто не изобразил)
#include <iostream>

using namespace std;

class functor
{
  mutable int state;
public:
  functor(int initial) : state(initial) {}
  void operator()() const { ++state; printf("state %d\n", state); }
};

void do_for_each(int count, const functor& action )
{
  while(--count >= 0)
    action();
}

int main()
{
    do_for_each( 3, functor(12) );
    return 0;
}


Если по каким-то причинам mutable не принимается, можно рядом завести в классе functor дополнительный член-ссылку на state. Тогда через эту ссылку можно будет модифицировать значение и в константном объекте. Правда прийдется самостоятельно реализовать семантику копирования (конструктор копии и копирующий оператор присваивания), но это тривиально. Использование класса functor при этом не меняется:
class functor
{
  int _state;
  int& state;
public:
  functor(int initial) : _state(initial), state(_state) {}
  functor(const functor& other) : _state(other._state), state(_state) {}

  functor& operator=(const functor& other) { _state = other._state; return *this;}
  
  void operator()() const { ++state; printf("state %d\n", state); }
};
--
Справедливость выше закона. А человечность выше справедливости.
Re: Вечный вопрос.
От: jazzer Россия Skype: enerjazzer
Дата: 20.08.09 00:20
Оценка: 13 (3) +3
Здравствуйте, c-smile, Вы писали...

Функтор с состоянием — плохая идея.
Правильный дизайн — состояние отдельно, функтор, управляющий им — отдельно.
Функтор в таком случае получается легковесный, его можно сколько угодно раз копировать (что нельзя сделать с твоим), а создание лишних временных копий может происходить.
Опять же, поскольку функтор легковесный (просто обертка), его оператор вызова будет константным.
И он будет дружить со стандартными и совместимыми с ними алгоритмами, которые все принимают функторы по значению.

Т.е. я бы это написал так:
class functor
{
  int& state_;
public:
  functor(int& state):state_(state) {}
  void operator()() const { ++state_; printf("state %d\n", state_); }
};

// или void do_for_each( int count, functor action )
void do_for_each( int count, const functor& action ) // line 13
{
  while(--count >= 0)
    action();
}

int main()
{
    int state = 12;
    do_for_each( 3, functor(state) ); // line 21
    return 0;
}


Все это элементарно генерализуется в нечто вроде (пишу в браузере, не компилировал)
template <class State, class R = void, R (State::*updater)() = State::operator()>
class StateUpdater
{
  State& state_;
public:
  StateUpdater(State& state):state_(state) {}
  R operator()() const { return (state_.*updater)(); }
};


Другое популярное решение — хранить состояние не по ссылке, а как shared_ptr<State>.
В любом случае, суть одна — состояние должно быть внешним по отношению к функтору.
Это позволяет алгоритму наплодить сколько угодно копий функтора, не вызывая каких-либо проблем с состоянием.
Плюс оно обеспечивает доступ к состоянию после окончания работы алгоритма, в отличие от твоего варианта (все-таки гораздо чаще состояние нужно, чем не нужно).
jazzer (Skype: enerjazzer) Ночная тема для RSDN
You will always get what you always got
  If you always do  what you always did
Re: Вечный вопрос.
От: Кодт Россия  
Дата: 20.08.09 10:06
Оценка: 2 (2) +1
Здравствуйте, c-smile, Вы писали:

Передавал бы ты этот функтор по значению, и не нахлобучивался. Аналогично тому, как ведёт себя std::for_each.
template<class F>
F n_times(int n, F f)
{
    for(int i=0; i!=n; ++i)
        f();
    return f;
}

А если твой функтор тяжеловесный — сделай его легковесным. Например, вытащи все тяжёлые данные из тела объекта, заменив на указатели (политика владения оными — на твоё усмотрение).
struct fun
{
    Heavy data;
    .....
};

Heavy data = for_each(begin,end,fun()).data; // куча копирований Heavy

/////////////////////////////

struct fun_light
{
    Heavy* data;
    explicit fun_light(Heavy* side) : data(side) {}
    .....
};

Heavy data;
for_each(begin,end,fun_light(&data));

////////////////////////////

struct fun_smart
{
    shared_ptr<Heavy> data;
    fun_smart() : data(new Heavy()) {}
    explicit fun_smart(shared_ptr<Heavy> side) : data(side) {}
    .....
};

shared_ptr<Heavy> data = for_each(begin,end,fun_smart()).data;

Это касается и рукодельных, и стандартных замыканий (bind / function).
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!