std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 15:14
Оценка: 1 (1)
Является ли следующий код валидным? И если нет, то почему?
    std::vector<int> arr;

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

    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;
    }
И каждый день — без права на ошибку...
Re: std::find_if<reference, predicate> можно?
От: VTT http://vtt.to
Дата: 23.03.15 15:51
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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


Мне кажется, что такой код — прямой прыжок в область implementation-defined или undefined behavior.
Тут даже не понятно, что вы имеете ввиду под "валидный": что он соберется? что значение it будет изменено в коде find_if? что значение it не будет изменено в коде find_if?
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re: std::find_if<reference, predicate> можно?
От: watchmaker  
Дата: 23.03.15 15:58
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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

Не является, если под валидностью подразумевается, что код скомпилируется и что после вызова find_if возвращённое значение совпадёт со значением it.
Re[2]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 16:03
Оценка:
Здравствуйте, VTT, Вы писали:

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

VTT>Мне кажется, что такой код — прямой прыжок в область implementation-defined или undefined behavior.
Почему?

VTT>Тут даже не понятно, что вы имеете ввиду под "валидный": что он соберется? что значение it будет изменено в коде find_if? что значение it не будет изменено в коде find_if?


Меня интересует результат и соответствие стандарту. Вот по стандарту он должен работать или нет?
И каждый день — без права на ошибку...
Re[2]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 16:04
Оценка:
Здравствуйте, watchmaker, Вы писали:

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

W>Не является, если под валидностью подразумевается, что код скомпилируется и что после вызова find_if возвращённое значение совпадёт со значением it.

Код не должен компилироваться? Почему? И почему значение может не совпадать?
И каждый день — без права на ошибку...
Re[3]: std::find_if<reference, predicate> можно?
От: watchmaker  
Дата: 23.03.15 16:17
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Здравствуйте, watchmaker, Вы писали:


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

W>>Не является, если под валидностью подразумевается, что код скомпилируется и что после вызова find_if возвращённое значение совпадёт со значением it.

BFE>И почему значение может не совпадать?


Нет, давай лучше ты сначала расскажешь, почему считаешь, что оно должно совпадать :)

Я вот не вижу в стандарте ни единого основания для такой уверенности. Это, разумеется, если смотреть как описан алгоритм find_if в стандарте, а не в художественном изложении на всяких сайтах в интернете вроде cplusplus.com. На последнем, например, написано, что поведение функции эквивалентно некой представленной реализации, хотя на самом деле такого требования нет и на практике реализация как раз отличается. Причём часть отличий как раз затрагивает процесс копирования итераторов. Хотя для равенства it и возвращаемого занчения важна детерминистичность этого процесса.
Отредактировано 23.03.2015 16:24 watchmaker . Предыдущая версия .
Re: std::find_if<reference, predicate> можно?
От: Mr.Delphist  
Дата: 23.03.15 16:43
Оценка:
Здравствуйте, 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>


Чего-то я не понимаю. Т.е. сначала Вы ищете итератор, указывающий на "элемент больше либо равный двух", затем, если нашлось, забываете про этот итератор и модифицируете первый элемент массива? Или Вы хотите модифицировать найденный элемент?

Стандарт вроде бы говорит что

25.2.5.1
Returns: The first iterator i in the range [first,last) for which the following corresponding conditions hold: *i == value, pred(*i) != false, pred(*i) == false. Returns last if no such iterator is found.


Так что код на первый взгляд выглядит нормальным. Странным, но нормальным.
Re[4]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 16:52
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>>>Не является, если под валидностью подразумевается, что код скомпилируется и что после вызова find_if возвращённое значение совпадёт со значением it.

BFE>>И почему значение может не совпадать?
W>Нет, давай лучше ты сначала расскажешь, почему считаешь, что оно должно совпадать

find_if в качестве возвращаемого значения имеет то же тип, что и итератор, поэтому, если передаётся ссылка, то должна быть возвращена легальная ссылка на найденный объект. Насколько я понимаю, не существует способа вернуть валидную ссылку на локальный объект. Значит ссылка на найденный аргумент и на первый параметер должны совпадать. .
И каждый день — без права на ошибку...
Re[2]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 16:56
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

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


MD>Чего-то я не понимаю. Т.е. сначала Вы ищете итератор, указывающий на "элемент больше либо равный двух", затем, если нашлось, забываете про этот итератор и модифицируете первый элемент массива?

Почему первый? Я же ссылку передал. Её же ожидаю в качестве результата. А какую ещё валидную ссылку может вернуть find_if?
Я ожидаю, что it будет указывать на найденный элемент.
И каждый день — без права на ошибку...
Re[3]: std::find_if<reference, predicate> можно?
От: VTT http://vtt.to
Дата: 23.03.15 17:13
Оценка:
Здравствуйте, B0FEE664, Вы писали:

VTT>>Мне кажется, что такой код — прямой прыжок в область implementation-defined или undefined behavior.

BFE>Почему?

BFE>Меня интересует результат и соответствие стандарту. Вот по стандарту он должен работать или нет?


В стандарте определяется только значение итератора, возвращаемого из функции, что именно делается с аргументами там не описывается.
В стандарте говорится, что первые два аргумента должны соответствовать требованиям для InputIterator, т.е. среди прочего быть CopyConstructible, CopyAssignable, и Destructible. Вообще говоря, я не уверен что InputIterator & соответствует этим требованиям.
Так или иначе, функция принимающая эти итераторы, может их копировать и присваивать (и предикат тоже!), как хочет (разрешение на копирование предиката там даже специально отмечено). Так что соответствующая стандарту реализация вполне может делать так:
...
find_if(InputIterator first, InputIterator last, Predicate pred)
{
    auto iter = first;
    while(iter != last)
    {
    if(pred(*iter))
        {
            break;
        }
        ++iter;
    }
    return(iter);
}

или даже так:
...
find_if(InputIterator first, InputIterator last, Predicate pred)
{
    auto iter = first;
    while(iter != last)
    {
    if(pred(*iter))
        {
            break;
        }
        ++iter;
    }
    first = last;
    return(iter);
}
Говорить дальше не было нужды. Как и все космонавты, капитан Нортон не испытывал особого доверия к явлениям, внешне слишком заманчивым.
Re[5]: std::find_if<reference, predicate> можно?
От: watchmaker  
Дата: 23.03.15 17:18
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Значит ссылка на найденный аргумент и на первый параметер должны совпадать. .

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

BFE>Насколько я понимаю, не существует способа вернуть валидную ссылку на локальный объект.

Тут скорее другой принцип: Garbage in, garbage out.
Re[4]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 17:41
Оценка:
Здравствуйте, VTT, Вы писали:

VTT>В стандарте определяется только значение итератора, возвращаемого из функции, что именно делается с аргументами там не описывается.

Ну, тип то его явно указан.

VTT>В стандарте говорится, что первые два аргумента должны соответствовать требованиям для InputIterator, т.е. среди прочего быть CopyConstructible, CopyAssignable, и Destructible. Вообще говоря, я не уверен что InputIterator & соответствует этим требованиям.


Я не знаю, может и нет...
На
    std::cout << "std::is_copy_assignable<std::vector<int>::iterator&>::value " << std::is_copy_assignable<std::vector<int>::iterator&>::value << std::endl;
    std::cout << "std::is_move_assignable<std::vector<int>::iterator&>::value " << std::is_move_assignable<std::vector<int>::iterator&>::value << std::endl;

Студия 2013 пишет это:

std::is_copy_assignable<std::vector<int>::iterator&>::value 1
std::is_move_assignable<std::vector<int>::iterator&>::value 1



VTT>Так или иначе, функция принимающая эти итераторы, может их копировать и присваивать (и предикат тоже!), как хочет (разрешение на копирование предиката там даже специально отмечено). Так что соответствующая стандарту реализация вполне может делать так:

VTT>
VTT>...
VTT>find_if(InputIterator first, InputIterator last, Predicate pred)
VTT>{
VTT>    auto iter = first;
VTT>...
VTT>    return(iter);
VTT>}
VTT>

VTT>или даже так:
VTT>
VTT>...
VTT>find_if(InputIterator first, InputIterator last, Predicate pred)
VTT>{
VTT>    auto iter = first;
VTT>...
VTT>    return(iter);
VTT>}
VTT>

Я бы сказал, что это ошибка — возвращение висячей ссылки.
И каждый день — без права на ошибку...
Re[6]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 23.03.15 17:50
Оценка:
Здравствуйте, watchmaker, Вы писали:

BFE>>Значит ссылка на найденный аргумент и на первый параметер должны совпадать. .

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

BFE>>Насколько я понимаю, не существует способа вернуть валидную ссылку на локальный объект.

W>Тут скорее другой принцип: Garbage in, garbage out.
Ну нет. Данные валидные. Более того, заставляют платить за то, что не используется: за копирование итераторов.
И каждый день — без права на ошибку...
Re: std::find_if<reference, predicate> можно?
От: Evgeny.Panasyuk Россия  
Дата: 23.03.15 18:59
Оценка: 4 (1)
Здравствуйте, 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>


Невалиден. Как минимум потому что нет специализации iterator_traits. find_if может иметь разные реализации для разных категорий iterator_traits<I>::iterator_category
Re: std::find_if<reference, predicate> можно?
От: Igore Россия  
Дата: 23.03.15 19:44
Оценка:
Здравствуйте, B0FEE664, Вы писали:

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

А может лучше так?
std::vector<int> test = {1,2,6,7};
std::find_if(std::begin(test), std::end(test), [](int& current){
   if (current >= 2)
   {
      current = 5;
      return true;
   }
   return false;
});
Re: std::find_if<reference, predicate> можно?
От: andyp  
Дата: 23.03.15 20:19
Оценка:
Здравствуйте, 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>


Может не скомпилироваться, если find_if внутри использует указатель на итератор. Или ссылку на итератор, если используешь старый С++, где reference collapsing не работает.
Re[3]: std::find_if<reference, predicate> можно?
От: Mr.Delphist  
Дата: 24.03.15 11:58
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Почему первый? Я же ссылку передал. Её же ожидаю в качестве результата. А какую ещё валидную ссылку может вернуть find_if?

BFE>Я ожидаю, что it будет указывать на найденный элемент.

Хорошо, давайте снова процитирую Стандарт, но более полно (если быт точным, то это draft n3290, но тут давно ничего не меняется):

25.2.5 Find [alg.find]
template<class InputIterator, class T> InputIterator find(InputIterator first, InputIterator last, const T& value);

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

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

1 Returns: The first iterator i in the range [first,last) for which the following corresponding conditions hold: *i == value, pred(*i) != false, pred(*i) == false. Returns last if no such iterator is found.

2 Complexity: At most last — first applications of the corresponding predicate.


Здесь ничего не говорится про взаимосвязь входных параметров и внутреннего tmp, бегущего по диапазону [first, last). Итого, Вы можете предполагать иммутабельность входных параметров, но никак не можете ожидать мутабельность — это ни из чего не следует. И даже если какая-то реализация даёт такой эффект, он именно side effect, implementation-defined.

Что касается ссылки, то у меня ощущение, что она сыграет лишь как "передача it по ссылке, а не copy construction". Ведь Вы сравниваете результат find_if() с итератором itEnd, а не указателем на него — и всё компилируется? Ну и опять же, что ссылка, что не ссылка — это будет соптимизировано любым уважающим себя компилятором, так что не ожидаю разницы в итоговом коде, если честно.
Re: std::find_if<reference, predicate> можно?
От: smeeld  
Дата: 24.03.15 12:29
Оценка:
Здравствуйте, B0FEE664, Вы писали:

На толкователя стандарта не претендую, но reference to object-это non-type template-parameter, а прототип find_if определён
с type template-parameter, поэтому строка std::find_if<std::vector<int>::iterator&, decltype(oFn)>(it, itEnd, oFn) есть
нарушение, так как содержит type-referеnce в параметрах шаблона. И вообще не понятно зачем такие качели, если копирование
итераторов по ссылке не есть дорогостоящая операция, можно смело делать it=std::find_if(itBeg, itEnd, oFn)
Re[4]: std::find_if<reference, predicate> можно?
От: B0FEE664  
Дата: 24.03.15 12:48
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>

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


MD>Здесь ничего не говорится про взаимосвязь входных параметров и внутреннего tmp, бегущего по диапазону [first, last). Итого, Вы можете предполагать иммутабельность входных параметров, но никак не можете ожидать мутабельность — это ни из чего не следует. И даже если какая-то реализация даёт такой эффект, он именно side effect, implementation-defined.


Я уже ответил здесь
Автор: B0FEE664
Дата: 23.03.15
, но могу дополнить. Если я вижу функцию, типа:
template<class T> T fun(T t);

то я считаю, что вправе рассчитывать на корректную работу этой функции с ссылками если обратное не оговорено в документации.
Т.е. вызов:
  int n = 32;
  ink k = fun<int&>(n);

должен отработать корректно. Корректно — это значит не иметь Undefined Behavior.
Согласны?

MD>Что касается ссылки, то у меня ощущение, что она сыграет лишь как "передача it по ссылке, а не copy construction". Ведь Вы сравниваете результат find_if() с итератором itEnd, а не указателем на него — и всё компилируется?

Компилируется или нет — зависит от реализации std::find_if

MD>Ну и опять же, что ссылка, что не ссылка — это будет соптимизировано любым уважающим себя компилятором, так что не ожидаю разницы в итоговом коде, если честно.

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

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

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

BFE>то я считаю, что вправе рассчитывать на корректную работу этой функции с ссылками если обратное не оговорено в документации.
BFE>Т.е. вызов:
BFE>
BFE>  int n = 32;
BFE>  ink k = fun<int&>(n);
BFE>

BFE>должен отработать корректно. Корректно — это значит не иметь Undefined Behavior.
BFE>Согласны?

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

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