Re[6]: std::find_if<reference, predicate> можно?
От: smeeld  
Дата: 24.03.15 13:17
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Копировать по ссылке, клонировать, делать swap или ещё что — это внутренняя кухня имплементации. Не закладывайтесь на неё.


Что если в реализации:

template<class InputIterator, class Predicate> InputIterator find_if(InputIterator first, InputIterator last, Predicate pred);

запихнут для своих целей такое

InputIterator& tmp=...

?
Re[2]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 13:27
Оценка:
Здравствуйте, smeeld, Вы писали:

S>На толкователя стандарта не претендую, но reference to object-это non-type template-parameter,

Вот ещё! Откуда такие странные идеи? Ссылка это вам не какая-нибудь константа!

S>а прототип find_if определён

S>с type template-parameter, поэтому строка std::find_if<std::vector<int>::iterator&, decltype(oFn)>(it, itEnd, oFn) есть
S>нарушение, так как содержит type-referеnce в параметрах шаблона. И вообще не понятно зачем такие качели, если копирование
S>итераторов по ссылке не есть дорогостоящая операция, можно смело делать it=std::find_if(itBeg, itEnd, oFn)

Ага, скажите ещё, что кроме векторов в find_if ничего нельзя подставить:
 Matrix a;
 Matrix b;
 Matrix c;

 Matrix d = std::find_if(a, b, [&c](const Matrix& m) { return det(m * с) == 1; }

Была бы фантазия!
И каждый день — без права на ошибку...
Re[3]: std::find_if<reference, predicate> можно?
От: smeeld  
Дата: 24.03.15 13:42
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Ага, скажите ещё, что кроме векторов в find_if ничего нельзя подставить:


Ну там выше больше постебался, а вообще с ссылками нужно поосторожней, это не такой
универсальный и безопасный инструмент как указатель. Выше в коментах написал про ненулевую
возможность появления в реализации строчки InputIterator& tmp, тогда что?
Re[6]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 13:52
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

BFE>>
BFE>>  ink k = fun<int&>(n);
BFE>>


MD>Согласен, безусловно. Более того, k и n имеют один и тот же тип. Но почему Вы ожидаете, что k и n должны быть взаимосвязаны по своему значению, если в специализации шаблона фигурирует символ ссылки на тип? Вот краеугольный камень, вокруг которого мы бродим уже второй день.

Потому, что для того, чтобы вернуть ссылку на что-то, это что-то должно существовать вне функции. Отсюда вывод: либо функция T& f<T&>(..) использует глобальную переменную, либо содержит в себе static переменную, либо мы имеем утечку памяти, либо fun<int&>(n) вернёт ссылку на n. (Либо мы имеем чёрное колдунство, которое мистическим способом позволяет продлить жизнь возвращённой ссылки до конца scope)
В любом случае, единственным разумным решением является вернуть переданную ссылку, так как в противном случае мы имеем либо side-effect, либо undefined behavior.

MD>Копировать по ссылке, клонировать, делать swap или ещё что — это внутренняя кухня имплементации. Не закладывайтесь на неё.

Невозможно клонировать ссылку. Можно дать ей другое имя, не более того.
И каждый день — без права на ошибку...
Re[4]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 13:57
Оценка:
Здравствуйте, smeeld, Вы писали:

BFE>>Ага, скажите ещё, что кроме векторов в find_if ничего нельзя подставить:

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

S>а вообще с ссылками нужно поосторожней, это не такой

S>универсальный и безопасный инструмент как указатель. Выше в коментах написал про ненулевую
S>возможность появления в реализации строчки InputIterator& tmp, тогда что?

Строчка вида:
InputIterator& tmp;

не допустима. Это вам не Forward iterators.

А строчка вида
InputIterator& tmp = ...;

чем плоха?
И каждый день — без права на ошибку...
Отредактировано 24.03.2015 13:59 B0FEE664 . Предыдущая версия .
Re[5]: std::find_if<reference, predicate> можно?
От: smeeld  
Дата: 24.03.15 14:07
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>
BFE>InputIterator& tmp = ...;
BFE>

BFE>чем плоха?

Всё банально, функция

template<class InputIterator, class Predicate> InputIterator find_if(InputIterator first, InputIterator last, Predicate pred);

в реализации, для удобства или для других целей, создают ссылку вида InputIterator& tmp=first.
За уши притянуто, но возможность такая есть, а ссылку на ссылку создавать нельзя, поэтому такое

find_if<std::vector<int>::iterator&,..., Con> find_if();


будет ошибкой.
Re[6]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 14:14
Оценка:
Здравствуйте, smeeld, Вы писали:

S>в реализации, для удобства или для других целей, создают ссылку вида InputIterator& tmp=first.

Мы говорим о текущем стандарте
Автор(ы): Владимиров Константин Игоревич
Дата: 28.05.2012
Освещены такие части нового стандарта, как rvalue references и lambda expressions. Подробно изложены относящиеся к ним вопросы, в том числе использование std::move и std::function
или о старом?
И каждый день — без права на ошибку...
Re[5]: std::find_if<reference, predicate> можно?
От: Igore Россия  
Дата: 24.03.15 14:49
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Я уже ответил здесь
Автор: B0FEE664
Дата: 23.03.15
, но могу дополнить. Если я вижу функцию, типа:

BFE>
BFE>template<class T> T fun(T t);
BFE>

BFE>то я считаю, что вправе рассчитывать на корректную работу этой функции с ссылками если обратное не оговорено в документации.
Обратное тоже верно, когда я пишу fun(T t), то подразумеваю что будет передано по значению, если я хочу менять аргумент я напишу fun(T& t)
Re[6]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 15:21
Оценка:
Здравствуйте, Igore, Вы писали:

I>Обратное тоже верно, когда я пишу fun(T t), то подразумеваю что будет передано по значению, если я хочу менять аргумент я напишу fun(T& t)

А почему так? Ведь T — это любой тип удовлетворяющий условиям написанным в документации для функции. Если вы не хотите менять параметр, то почему не написать fun(const T& t)?
И каждый день — без права на ошибку...
Re[2]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 15:48
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Невалиден. Как минимум потому что нет специализации iterator_traits.

Разве наличие iterator_traits является обязательным?

EP>find_if может иметь разные реализации для разных категорий iterator_traits<I>::iterator_category


Значит ли это, что если я напишу (добавлю код):
template<> struct std::iterator_traits<std::vector<int>::iterator&> {
typedef std::ptrdiff_t difference_type;
typedef int value_type;
typedef int* pointer;
typedef int& reference;
typedef std::input_iterator_tag iterator_category;
};


то find_if должен заработать?
И каждый день — без права на ошибку...
Re[7]: std::find_if<reference, predicate> можно?
От: smeeld  
Дата: 24.03.15 21:56
Оценка:
,Здравствуйте, B0FEE664, Вы писали:

BFE>Мы говорим о текущем стандарте или о старом?



Если серьёзно, то тема топика есть проблема на ровном месте. В стандарте есть рекомендации по
менеджменту возвращения объектов из функций-фабрик. Те, что имеют прототипы

template <typename T>
T func(...);


Эти рекомендации реализованиы во всех известных компиляторах. И когда делаете так:

std::vector<int>::iterator it = arr.begin();
std::vector<int>::iterator itEnd = arr.end();
auto oFn = [](const int& n) { return 2 <= n; };
if ( std::find_if<std::vector<int>::iterator&, decltype(oFn)>(it, itEnd, oFn) != itEnd )
{
*it = 5;
}

Это эквивалентно этому

auto oFn = [](const int& n) { return 2 <= n; };
std::vector<int>::iterator itEnd = arr.end();
std::vector<int>::iterator it= std::find_if(arr.begin(), itEnd, oFn);
if(it!=itEnd)
{
*it=5;
};


Любой компилятор здесь сначала создаст и проинициализирует itEnd, потом создаст it, проинициализирует его it=arr.begin()
потом вызовется find_if, с передачей объектов it и itEnd по их адресам в стеке той функции, что вызывает find_if,
из find_if возвратится (значение rax на x86_64) тот же адрес объекта it, состояние которого в функции find_if
было изменено с arr.begin() на соответствующее результатам поиска.
Что до поиска истин в стандарте, то это занятия не благородное.
Re[7]: std::find_if<reference, predicate> можно?
От: Igore Россия  
Дата: 25.03.15 07:07
Оценка:
Здравствуйте, B0FEE664, Вы писали:

I>>Обратное тоже верно, когда я пишу fun(T t), то подразумеваю что будет передано по значению, если я хочу менять аргумент я напишу fun(T& t)

BFE>А почему так? Ведь T — это любой тип удовлетворяющий условиям написанным в документации для функции. Если вы не хотите менять параметр, то почему не написать fun(const T& t)?
Согласен, для параметров в основном это (const T&, T*), просто T обычно для результата или объявления, и в этих случаях я ссылку не ожидаю.
Что то вроде такого.
template< class T >
class Test
{
public:
   Test( const T& t ) : t_(t){}
   T getT() const {
      return t_;
   }
private:
   T t_;
};
Re[2]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 25.03.15 10:23
Оценка:
Здравствуйте, Igore, Вы писали:

BFE>>Является ли следующий код валидным? И если нет, то почему?

I>А может лучше так?
I>
I>std::vector<int> test = {1,2,6,7};
I>std::find_if(std::begin(test), std::end(test), [](int& current){
I>   if (current >= 2)
I>   {
I>      current = 5;
I>      return true;
I>   }
I>   return false;
I>});
I>

Нет, не лучше. Меня интересует вызов find_if с ссылкой. А если "улучшать" этот пример. то тогда уж так:
    std::vector<int> test = {1,2,6,7};
    for(auto& current : test)
    {
       if ( 2 <= current )
       {
          current = 5;
          break;
       }
    };
И каждый день — без права на ошибку...
Re[8]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 25.03.15 11:48
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Если серьёзно, то тема топика есть проблема на ровном месте.

S>В стандарте есть рекомендации по
S>менеджменту возвращения объектов из функций-фабрик. Те, что имеют прототипы
S>

S>template <typename T>
S>T func(...);

Это в каком пункте? Или по каким ключевым словам искать?

S>Эти рекомендации реализованиы во всех известных компиляторах. И когда делаете так:

  Скрытый текст
S>

S>std::vector<int>::iterator it = arr.begin();
S>std::vector<int>::iterator itEnd = arr.end();
S>auto oFn = [](const int& n) { return 2 <= n; };
S>if ( std::find_if<std::vector<int>::iterator&, decltype(oFn)>(it, itEnd, oFn) != itEnd )
S> {
S> *it = 5;
S> }

S>Это эквивалентно этому
S>

S>auto oFn = [](const int& n) { return 2 <= n; };
S>std::vector<int>::iterator itEnd = arr.end();
S>std::vector<int>::iterator it= std::find_if(arr.begin(), itEnd, oFn);
S>if(it!=itEnd)
S> {
S> *it=5;
S> };


В том-то и дело, что, например, для gcc 4.7.3 тут нет эквивалентности.

S>Любой компилятор здесь сначала создаст и проинициализирует itEnd, потом создаст it, проинициализирует его it=arr.begin()

S>потом вызовется find_if, с передачей объектов it и itEnd по их адресам в стеке той функции, что вызывает find_if,
S>из find_if возвратится (значение rax на x86_64) тот же адрес объекта it, состояние которого в функции find_if
S>было изменено с arr.begin() на соответствующее результатам поиска.
S>Что до поиска истин в стандарте, то это занятия не благородное.

Ну, если посмотреть на реализацию, то всё совсем не так:

  /**
   *  @brief Find the first element in a sequence for which a
   *         predicate is true.
   *  @ingroup non_mutating_algorithms
   *  @param  __first  An input iterator.
   *  @param  __last   An input iterator.
   *  @param  __pred   A predicate.
   *  @return   The first iterator @c i in the range @p [__first,__last)
   *  such that @p __pred(*i) is true, or @p __last if no such iterator exists.
  */
  template<typename _InputIterator, typename _Predicate>
    inline _InputIterator
    find_if(_InputIterator __first, _InputIterator __last,
        _Predicate __pred)
    {
      // concept requirements
      __glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
      __glibcxx_function_requires(_UnaryPredicateConcept<_Predicate,
          typename iterator_traits<_InputIterator>::value_type>)
      __glibcxx_requires_valid_range(__first, __last);
      return std::__find_if(__first, __last, __pred,
                std::__iterator_category(__first));
    }

И если задать ссылку, то будет ошибка:
stl_algo.h:4490:41: error: invalid initialization of non-const reference of type ‘__gnu_cxx::__normal_iterator<...>&’ from an rvalue of type ‘__gnu_cxx::__normal_iterator<...>’
в последней строке функции.
Но дело даже не в этом, а в том, что __first будет передан в __find_if по значению...
И каждый день — без права на ошибку...
Re[9]: std::find_if<reference, predicate> можно?
От: smeeld  
Дата: 25.03.15 13:41
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Это в каком пункте? Или по каким ключевым словам искать?


[class.temporary]

[ Example: Consider the following code:
class X {
public:
X(int);
X(const X&);
X& operator=(const X&);
~X();
};
class Y {
public:
Y(int);
Y(Y&&);
~Y();
};
X f(X);
Y g(Y);
void h() {
X a(1);
X b = f(X(2));
Y c = g(Y(3));
a = f(a);
};
An implementation might use a temporary in which to construct X(2) before passing it to f() using X’s
copy constructor; alternatively, X(2) might be constructed in the space used to hold the argument. Likewise,
an implementation might use a temporary in which to construct Y(3) before passing it to g() using Y’s
move constructor; alternatively, Y(3) might be constructed in the space used to hold the argument. Also,
a temporary might be used to hold the result of f(X(2)) before copying it to b using X’s copy constructor;
alternatively, f()’s result might be constructed in b. Likewise, a temporary might be used to hold the result
of g(Y(3)) before moving it to c using Y’s move constructor; alternatively, g()’s result might be constructed
in c. On the other hand, the expression a=f(a) requires a temporary for the result of f(a), which is then
assigned to a. — end example ]


BFE>В том-то и дело, что, например, для gcc 4.7.3 тут нет эквивалентности.


BFE>Ну, если посмотреть на реализацию, то всё совсем не так:


Загляните в ассемблерный листинг, проследите передаваемые в функцию find_if объекты, и где они
находятся (находятся в стеке вызывающей функции ) компилятор максимально ориентирован на сохранение
объектов в пределах одной области памяти, передавая их в функции по указателям. Ваши попытки передать
ссылку полностью повторят то, что компилятор делает автоматически. Только с ссылками можно нарваться на
различные ошибки компиляции.
Re: std::find_if<reference, predicate> можно?
От: Кодт Россия  
Дата: 25.03.15 16:59
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Является ли следующий код валидным? И если нет, то почему?

BFE>
BFE>    std::vector<int> arr;

BFE>    arr.push_back(0); arr.push_back(1); arr.push_back(2);

BFE>    std::vector<int>::iterator it    = arr.begin();
BFE>    std::vector<int>::iterator itEnd = arr.end();

BFE>    auto oFn = [](const int& n) { return 2 <= n; };

BFE>    if ( std::find_if<std::vector<int>::iterator&, decltype(oFn)>(it, itEnd, oFn) != itEnd )
BFE>    {
BFE>        *it = 5;
BFE>    }
BFE>


Валидный он или нет, но, определённо, это хак.
Можно заметить, что все алгоритмы, принимающие input iterator (а не только forward iterator и далее), должны работать нормально.
Потому что в каждый момент времени существует единственный рабочий экземпляр input iterator'а на данном диапазоне, а нерабочие копии игнорируются.
Таким образом, инициализация и присваивание (то, на чём ссылки отличаются от значений) здесь суть одно.
template<class It>
It foo(It i)
{
  ++i; // с этого момента экземпляр It, остававшийся в вызывающей стороне, условно-невалиден
  It j = i;
  ++j; // с этого момента i условно-невалиден
  return j;
}

foo(istream_iterator<int>(cin));

vector<int> arr(100);
vector<int>::iterator i = arr.begin();
i = foo(i);
foo<vector<int>::iterator&>(i);


Для forward iterator это уже не так
template<class It>
It foo(It i, It end)
{
  It j = i;
  if(j != end)
  {
    ++j;
    assert(i != j); // если It чистый input iterator, то сравнивать i больше нельзя
    if(j != end) // а вот end - сколько угодно
    {
      ++j;
      assert( distance(i,j)==2 ); // если It чистый input, то использовать его в distance бессмысленно и вредно
    }
  }
  return j;
}

template<class It>
It middle(It i, It end)
{
  advance(i,
    distance(i,end)/2 // внутри distance мы изменили локальную копию i
  ); // поэтому передаваемая в advance ссылка должна быть на валидный итератор
  return i;
}


Ну а в твоём случае — вместо хака есть более чистое решение
if( (it = find_if(it,itEnd,oFn)) != itEnd )
  .....

Оно делает РОВНО то же самое, но избавлено от заморочек.

Если такая идиома встречается регулярно, ну запиши её
template<class It, class V>
bool skip_unequal(It& it, It end, V const& v) { it = find(it,end,v); return it != end; }

template<class It, class Pred>
bool skip_unless(It& it, It end, Pred pred) { it = find_if(it,end,pred); return it != end; }
Перекуём баги на фичи!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.