#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 21return 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 21return 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, constfunctor& action ) // line 13
{
while(--count >= 0)
action();
}
Здравствуйте, 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.
Здравствуйте, c-smile, Вы писали:
CS>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?
Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал.
Насколько я знаю приведенный код абсолютно легален с точки зрения стандарта. Если нет то также буду признателен если кто-нибудь скажет почему.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, c-smile, Вы писали:
CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?
CS>Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал. CS>Насколько я знаю приведенный код абсолютно легален с точки зрения стандарта. Если нет то также буду признателен если кто-нибудь скажет почему.
Временным объектом нельзя инициализировать lvalue ref, жди rvalue ref (&&)
Здравствуйте, 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.
CS>>Как этот GCC уговорить чтобы он понял что я ему хочу сказать?
CS>Да и был бы признателен если бы кто-нить указал мне флаг для GCC который бы это параноидальное поведение выключал. CS>Насколько я знаю приведенный код абсолютно легален с точки зрения стандарта. Если нет то также буду признателен если кто-нибудь скажет почему.
Здравствуйте, 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>
Спасибо. А какой-нить другой стандарт у вас товарищи есть в котором вот эти два фрагмента
Здравствуйте, c-smile, Вы писали:
CS>Спасибо. А какой-нить другой стандарт у вас товарищи есть в котором вот эти два фрагмента CS>были бы эквивалентны? Или чтобы не надо было mutable members городить на пустом месте...
CS>Нет хэппи в лайфе, хоть фэйсом об тэйбл.
Можно добавить в functor функцию
functor& self() { return *this; }
и вызывать так:
do_for_each( 3, functor(12).self() );
self возвращает уже lvalue reference, поэтому сработает1.
Если по каким-то причинам mutable не принимается, можно рядом завести в классе functor дополнительный член-ссылку на state. Тогда через эту ссылку можно будет модифицировать значение и в константном объекте. Правда прийдется самостоятельно реализовать семантику копирования (конструктор копии и копирующий оператор присваивания), но это тривиально. Использование класса functor при этом не меняется:
Функтор с состоянием — плохая идея.
Правильный дизайн — состояние отдельно, функтор, управляющий им — отдельно.
Функтор в таком случае получается легковесный, его можно сколько угодно раз копировать (что нельзя сделать с твоим), а создание лишних временных копий может происходить.
Опять же, поскольку функтор легковесный (просто обертка), его оператор вызова будет константным.
И он будет дружить со стандартными и совместимыми с ними алгоритмами, которые все принимают функторы по значению.
Т.е. я бы это написал так:
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 21return 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>.
В любом случае, суть одна — состояние должно быть внешним по отношению к функтору.
Это позволяет алгоритму наплодить сколько угодно копий функтора, не вызывая каких-либо проблем с состоянием.
Плюс оно обеспечивает доступ к состоянию после окончания работы алгоритма, в отличие от твоего варианта (все-таки гораздо чаще состояние нужно, чем не нужно).
Передавал бы ты этот функтор по значению, и не нахлобучивался. Аналогично тому, как ведёт себя 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).