2009-04-02 5 views
4

Я реализацию UnaryOperation как этотC++ станд :: преобразовать побочный эффект

struct Converter 
{ 
    Converter(std::size_t value): 
     value_(value), i_(0) 
    {} 
    std::string operator() (const std::string& word) 
    { 
     return (value_ & (1 << i_++)) ? 
      word: 
      std::string(word.size(), ' '); 
    } 
    std::size_t value_; 
    std::size_t i_; 
}; 

И я использую его как

std::vector v; 
// initialization of v 
std::transform(v.begin(), v.end(), 
       std::back_inserter(result), 
       Converter(data)); 

Мой вопрос это я могу рассчитывать на мое предположение, что алгоритм вызовет мой «оператор конвертера»() в том порядке, в котором «Converter :: i_» будет соответствовать числу элементов в «v».

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

Спасибо.

Edit:

Я осведомлен о требованиях «не побочный эффект» в стандарте для алгоритма преобразования. Я не могу найти то, что является «побочным эффектом» для функторов в том же стандарте.

Возможно, есть какое-то красивое решение для этой задачи?

+0

Фактически, примечание о побочном эффекте больше не появляется в текущей черновике. но вместо этого он говорит, что алгоритмы могут свободно копировать свои объекты функций, если не указано иное. поэтому я думаю, что «побочные эффекты» означают изменение членов функционального объекта. –

ответ

6

Qute от стандарта:

25.2.3 Transform [lib.alg.преобразование]
Требуется:
Операции op и binary_op не должны иметь побочных эффектов.

Side Effect (wikipedia definition)

В вашем случае мы имеем следующий побочный эффект:

Converter c(data); 
c(some_const_value) != c(some_const_value); 

У вас нет никаких гарантий относительно алгоритмов, но я верю, что он будет работать практически на всех реализаций СТЛ ,

Предложенное решение
Кажется, я знаю один способ сделать то, что вам нужно:
использовать повышение :: counting_iterator - для перебрать двух контейнеров;

это будет выглядеть так:

bool bit_enabled(size_t data, unsigned char number) 
{ 
    return (data & 1 << number) != 0; 
} 

std::string select_word( 
       const std::string& word, 
       size_t data, 
       size_t number) 
{ 
    return bit_enabled(data, number) ? word : std::string(' ', word.length()); 
} 

const size_t data = 7; 
const boost::array< std::string, 3 > vocabulary = { "a", "b", "c" }; 
std::vector<std::string> result; 
std::transform(
    vocabulary.begin(), 
    vocabulary.end(), 
    boost::counting_iterator<size_t>(0), 
    back_inserter(result), 
    boost::bind(&select_word, _1, data, _2) 
); 

Кроме того, может быть, если вы будете определять битовый итератор или будете использовать некоторый битовый контейнер вы можете использовать повышение :: zip_iterator для итерации оба контейнера.

РЕДАКТИРОВАТЬ:
за вчера я нашел interest article, которые содержат определение побочного эффекта в соответствии со стандартом.

Стандарт определяет побочный эффект, как следующим образом: Доступ к объекту назначенных летучим именующим, модифицирующего объект, вызывая библиотеку функцию ввод/вывода, или вызов функции , что делает любой из тех, операции все побочные эффекты, которые являются изменениями в состоянии исполнения окружающей среды.

EDIT:
Я надеюсь, что это будет последнее редактирование.
Я всегда утверждаю, что «нет побочного эффекта» означает:
f (a) должно быть равно f (a) всегда. (f зависит от среды исполнения: memory/cpu/global variables/member variables, как в вашем случае и т. д.).
«Не производить побочный эффект» означает - не меняйте среду исполнения.

Но в стандарте C++ у нас есть более низкий уровень отказа от побочного эффекта.

Что вы делаете в своем примере под названием Состояния Функтор.
Стандарт не говорит о функциях «Statefull», но также не говорит о количестве копий вашего функтора - вы не могли использовать этот трюк, потому что это неуказанное поведение.

список См стандартной библиотеки вопросы (аналогичный вопрос для predicat):
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-active.html#92

+0

Спасибо за объяснение «побочного эффекта». –

+0

Nice counting_iterator! Трансформация может использовать два контейнера. Вы измените решение еще раз? –

+0

Я также считал, что определение побочного эффекта. но в этом контексте это не имеет никакого смысла. представьте, что вы делаете это в operator(): {int a; a = 4; return T (t, a); } будет побочным эффектом (изменение локальной переменной «a») и, таким образом, не допускается спецификацией преобразования. –

0

Да и нет. Это связано с тем, что вы подаете итераторы на свой объект Converter. Для контейнера последовательности, такого как vector, вы получаете i, чтобы соответствовать порядку элементов. Однако для ассоциативных контейнеров, таких как map/multimap/set/multiset, это может быть (возможно, не будет) действительным.

+0

Алгоритм для набора по-прежнему использует выходной итератор для добавления новых элементов. Поэтому он берет один и нажимает один на выход. Я сомневаюсь, что «UnaryOperation не будет иметь никаких побочных эффектов». –

2

Я только что проверил стандарт и, если я правильно понял, ответ отрицательный. Описание «преобразования» имеет следующие дополнительные требования (25.2.3):

Требуется: оп и binary_op не имеет каких-либо побочных эффектов.

уходящие в глубины моей памяти, я помню разговор дал Nicolai Josuttis на ACCU конференции, где он показал, что для конкретного типа контейнера и реализации STL, функция объект был скопирован. Éric предоставил this ссылку на статью доктора Добба, в которой обсуждаются различия более подробно.

EDIT: Альтернативное решение:

Алгоритм for_each не имеют этого ограничения, так что вы можете изменить свой объект конвертер взять ссылку на результат вектора и сделать push_back внутри функции преобразователя.

struct Converter 
{ 
    Converter(std::size_t value, std::vector<std::string> & result): 
     value_(value), i_(0), result_(result) 
    {} 
    void operator() (const std::string& word) 
    { 
    result_.push_back (value_ & (1 << i_++)) ? 
      word: 
      std::string(word.size(), ' '); 
    } 
    std::size_t value_; 
    std::size_t i_; 
    std::vector<std::string> & result_; 
}; 

И использовать for_each вместо преобразования:

std::vector v; 
// initialization of v 
std::for_each (v.begin() 
       , v.end(), 
       Converter(data, result)); 
+0

Спасибо за объяснение побочного эффекта. –

+0

«На практике я думаю, это может означать ...» Где я могу это доказать? –

+0

Erm .... хорошо - я могу только утверждать, что узнал об этом после того, как я посетил конференцию ACCU в Оксфорде, где Николас Йосуттис пообщался с настоящим примером кода. Быстрый поиск в Google обнаружил следующее: http://learningcppisfun.blogspot.com/2007/02/i-was-discussing-about-problem-here.html –

0

Я считаю, что есть некоторые случаи, когда ответ да, но есть также определенные случаи, когда это нет.Не имея возможности вспомнить, какой из них (извините), и сделать код менее хрупким, я бы не стал полагаться на него и не давал бы мне оставаться в стороне от функтора. т.е. внутри Converter сделать:

Converter(std::size_t value, std::size_t& i): value_(value), i_ (i) {}
и
std::size_t &i_;

затем вызвать с

std::vector v;
std::size_t i(0);
// initialization of v
std::transform(v.begin(), v.end(), std::back_inserter(result), Converter(data, i));

Грязный, но безопаснее.

1

В качестве примера случая, когда побочные эффекты были бы определенными плохой вещью, рассмотрим гипотетическую параллельную реализацию STL, которая разделяет работу между несколькими ЦП.

Я считаю, что это было в умах авторов STL. Оригинальный STL был из SGI, одного из более крупных имен при построении массивно параллельных одномодовых и кластерных систем.

+0

Интересное предположение о «параллельном STL». Мне нужно какое-то доказательство. Цитата из стандарта о копировании или что-то в этом роде. –

Смежные вопросы