Есть ли что-то вроде std::value_wrapper параллельно std::reference_wrapper?

(ОБНОВЛЕНИЕ: этот вопрос связан с реализацией класса-оболочки, передаваемого по значению для объекта, который имеет разные значения для const Foo и Foo, на основе перемещения исключительно на твердом мнении людей здесь. До этого я раздавал const Foo* и Foo*, а когда пришла обертка, я поменял ее местами на Wrapper<Foo> и const Wrapper<Foo>. Теперь ясно, что механическая замена не имеет смысла, и мне понадобится что-то более сложное, например Wrapper<Foo> и Wrapper<const Foo>. ... хотя я пока не знаю, как это правильно написать. Извините за недоразумение, но я оставлю это здесь, так как на самом деле думаю, что это более показательно, чем многие вопросы.)


При рассмотрении этого вопроса казалось, что он сводится к будучи параллельным идее, что вы не можете этого сделать:

const Foo defaultFoo (6502);
const Foo theFoo (getConstFoo()); // returns const Foo for privilege reasons
if (theFoo.getBar() < 2012) {
    theFoo = defaultFoo; // Error.
}
// ...you want to do const-safe methods with theFoo...

Подобно ссылкам, константное значение не может быть перенацелено. Выполнение следующего будет скомпилировано, но не будет тем, что я (в этом сценарии) намеревался:

Foo defaultFoo (6502);
Foo& theFooRef (getFooRef());
if (theFooRef.getBar() < 2000) {
    theFooRef = defaultFoo; // Not an error, but not a retarget.
}
// ...you want to do stuff with theFooRef...

Кажется (насколько я понимаю), что reference_wrapper может обойти это в эталонном случае с чем-то подобно:

Foo defaultFoo (6502);
std::reference_wrapper<Foo> theFooRef (getFooRef());
if (theFooRef.get().getBar() < 2000) {
    theFooRef = std::ref(defaultFoo);
}
// ...do stuff with theFooRef.get() or employ implicit cast...

Мне интересно, есть ли там "value_wrapper", который делает что-то подобное. Мне кажется разумным хотеть переменную, которая содержит элемент по значению, которое является константой, по причинам константной правильности... не потому, что вы не собираетесь ее менять. (например, отслеживание текущего узла в обходе дерева предварительного заказа, несмотря на наличие только постоянного доступа к узлам в этом дереве, где передача предыдущего узла в функцию — это то, как вы получаете новый узел)

Если вы хотите быть неуклюжим, вы можете использовать std::pair<const Foo, bool> и просто игнорировать bool:

const Foo defaultFoo (6502);
std::pair<const Foo, bool> theFooBool (getConstFoo(), false);
if (theFooBool.first.getBar() < 2012) {
    theFooBool = std::pair<const Foo, bool> (defaultFoo, false);
}
// ...do const-safe methods with theFooBool.first...

Но есть ли лучший способ решить эту проблему, помимо реализации моей собственной версии "value_wrapper"?


person HostileFork says dont trust SE    schedule 12.07.2012    source источник
comment
Я не понимаю. Назначая theFoo = defaultFoo, вы меняете значение theFoo. Поэтому объявлять его const неправильно. Если вам нужно иметь возможность изменить значение чего-либо, не объявляйте это const. Если вы действительно хотите сделать theFoo константным и вам нужно иметь возможность выбирать тот или иной Foo при инициализации theFoo, используйте функцию или лямбда-выражение для инкапсуляции логики выбора.   -  person James McNellis    schedule 13.07.2012
comment
Я думаю, вы в корне неправильно понимаете, что такое константная правильность ... Кроме того, тот факт, что getConstFoo возвращает Foo const, не означает, что theFoo само должно быть константным; вы выполняете логическую копию, поэтому не имеет значения, является ли источник копии константным или нет.   -  person ildjarn    schedule 13.07.2012
comment
Кому @JamesMcNellis и ildjarn: извините, но ваши ответы кажутся мне параллельными тому, что кто-то говорит: «Если это ссылка, вы никогда не переназначаете ее, так ссылки не работают!» Читать книгу! Но люди приходят и имеют reference_wrappers, потому что они находят мотивирующие сценарии для таких изобретений. Поэтому, пожалуйста, поработайте со мной над сценарием вместо того, чтобы предполагать, что я не понимаю корректность констант (!) :-/ Мне удалось использовать лямбда-выражения, чтобы обойти некоторую логику условной инициализации... но подумайте о случае обхода предварительного заказа с использованием константного значения (которое само по себе является оболочкой).   -  person HostileFork says dont trust SE    schedule 13.07.2012
comment
Между прочим, я вынужден пойти по этому пути, потому что люди разозлились, когда я попытался ухитриться не создать класс-оболочку, который передает ссылки по значению. Как следствие, эти константные или неконстантные объекты-оболочки представляют собой настоящую проблему с точки зрения сохранения исходной корректности констант для объектов ... которые прекрасно управлялись, когда они были указателями с псевдонимами. Если я превращу их в ConstWrapper и Wrapper, то я действительно откажусь от функций языка. кастинг-только-без-имея-ко-всем" title="создавать взаимозаменяемые типы классов с помощью только кастинга указателей без необходимости всех"> stackoverflow.com/questions/11219159/   -  person HostileFork says dont trust SE    schedule 13.07.2012
comment
@ildjarn Аргх. Ну, может быть, тогда вы правы, и я не понимаю, как эти объекты-оболочки по значению будут сохранять константную правильность. Можете ли вы прочитать между строк и сказать мне, что я делаю хочу, случайно? :-/ Основная цель состоит в том, что я хочу иметь два разных уровня доступа, и раньше это работало, но я предполагаю, что семантический переключатель по значению возлагает ответственность на человека, который возвращает значение, а не на человека, возвращающего его в договор.   -  person HostileFork says dont trust SE    schedule 13.07.2012
comment
Что-то вроде... кажется, что вы хотите немного изменить объект, затем сделать его константным. Это можно сделать либо путем привязки неконстантного объекта к константной ссылке, а затем только после этого работать с этой константной ссылкой, либо с помощью лямбда/фабрики для создания правильного объекта в первую очередь, как предложил @James. (Кроме того, несвязанный, но возвращающий const UDT из функций, как это делает getConstFoo, является плохой идеей, поскольку он препятствует семантике перемещения.)   -  person ildjarn    schedule 13.07.2012
comment
Я не хочу изменять объект. Я раньше использовал указатели, которые были либо на константные, либо на неконстантные объекты. Если кто-то вернул вам const Foo*, вы могли вызывать только константные методы. Если кто-то вернул вам Foo*, вы могли бы вызвать как константу, так и неконстанту. Вы можете передать Foo* методу, который принимает const Foo*, но не наоборот. Теперь у меня есть объект-оболочка, который копирует по значению, и похоже, что он действительно разрушает контракт, именно так я и заметил. Я мог бы использовать хорошую ссылку на любое место, где была сделана оболочка, сохраняющая контракт const...!   -  person HostileFork says dont trust SE    schedule 13.07.2012
comment
Обратите внимание, что строго говоря, это не то, для чего был разработан std::reference_wrapper. Указатели обеспечивают косвенность и переназначаются. Нет ничего плохого в использовании std::reference_wrapper, но вы говорите, что выполнение работы указателя — это что-то экзотическое.   -  person Luc Danton    schedule 13.07.2012
comment
Мне жаль, если вы восприняли мой комментарий как легкомысленный; это вовсе не было моим намерением. Я намеревался только получить разъяснения. Я думаю, что вопрос имеет немного больше смысла в контексте другого вопроса, который вы связали.   -  person James McNellis    schedule 13.07.2012
comment
@JamesMcNellis Не проблема, просто трудно писать здесь вопросы и чувствовать, что люди слишком быстро понимают, что ты идиот! минус, хлоп! кнопку триггера (независимо от того, так это на самом деле или нет). :-/ Я думаю, что проблема так же проста, как осознание того, что такие вещи, как необязательные, являются значениями, которые (для копируемых объектов) не обеспечивают шаг косвенности, который необходимо иметь, чтобы использовать константность в качестве контракта . Если объект является копируемым, этот объект не может иметь сам атрибут const и ожидать каких-либо рычагов. Отсюда загадочный ответ: зачем перенацеливать константу?   -  person HostileFork says dont trust SE    schedule 13.07.2012
comment
@LucDanton Я так понимаю, что основная цель reference_wrapper — сделать его копируемым, чтобы вы могли использовать его в контейнерах и т. д. Кажется, что возможность переназначения значения исходит из того, что его тип не является необработанной ссылкой и не является константой... хотя объекты могут отключить его (например, boost::Optional). Не знаю насчет экзотики, но С++ полон миллиона вещей, которые в зависимости от вашего опыта могут показаться тривиальными или сложными, это определенно ситуация слепого человека и слона.   -  person HostileFork says dont trust SE    schedule 13.07.2012


Ответы (2)


Если вы хотите быть неуклюжим, вы можете использовать std::pair и просто игнорировать логическое значение:

Это ясно объясняет, почему то, что вы хотите, невозможно сделать, потому что этот код не работает. Я использовал const int, а не const Foo. , но это та же идея. Вот эта строка обрывается:

theFooBool = std::pair<const Foo, bool> (defaultFoo, false);

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

Конечно, вы можете просто использовать std::reference_wrapper<const T>, что даст вам const доступ, но позволит выполнить повторную привязку. Конечно, он не предлагает семантику значений, но это самое близкое, что вы собираетесь получить. В общем, большинству людей это не нужно или даже не нужно, так что это просто еще не придумали.

person Nicol Bolas    schedule 13.07.2012
comment
Спасибо за хороший ответ и за то, что разобрались с компилятором... Я получил что-то очень близкое к компиляции, но я немного напряжен и просто псевдокодирую, чтобы попытаться выразить более важный вопрос: эй, что происходит здесь. Реальный ответ заключается в том, что мне нужно выяснить, как поместить const внутри моего объекта-оболочки, передаваемого по значению, куда-то, а не в значение самой оболочки. Любое подобие того, что это работает, было только иллюзией из-за того, что клиентский код уже соответствовал и самоподдерживал константность, никакой реальной защиты. :-/ - person HostileFork says dont trust SE; 13.07.2012
comment
Я думаю, что то, о чем я спрашиваю, технически возможно - переназначаемая оболочка, позволяющая вызывать константные методы только для содержащихся константных объектов. Но я, возможно, понимаю причину того, почему люди не считают, что это стоит реализовать в мом ответе на вопрос, почему boost::Optional выиграл не перенацеливать, когда содержащийся тип является const (обратная связь приветствуется). Любая реализация не будет более эффективной, чем конструкция копирования, которую необходимо определить для работы оболочки. Поэтому большинство людей просто ввели бы новую неконстантную переменную и продолжили бы жить дальше. (?) - person HostileFork says dont trust SE; 18.07.2012

Мои извинения, если я что-то упустил из виду здесь. Но поскольку в вашем вопросе об этом не упоминается, мне интересно, знаете ли вы и подумали ли:

Foo defaultFoo (6502);
std::reference_wrapper<const Foo> theFooRef (getFooRef());
if (theFooRef.get().getBar() < 2000) {
    theFooRef = std::cref(defaultFoo);
}
// ...do stuff with theFooRef.get() or employ implicit cast...
person Howard Hinnant    schedule 13.07.2012
comment
Спасибо; reference_wrapper является новым для меня, я встречаю много новых идей, когда я проверить предложения по использованию оболочки с передачей по значению для чувствительного к константе объекта. Это непросто, как и со многими шаблонами проектирования. Я упомянул то, что, по моему мнению, является основной причиной путаницы: я не хочу const Wrapper<Foo> против Wrapper<Foo>, я хочу Wrapper<const Foo> и Wrapper<Foo>, которые сложнее понять, как писать. :-/ Любые советы приветствуются! - person HostileFork says dont trust SE; 13.07.2012
comment
@HostileFork: написать template<typename T> struct Wrapper; и частичную специализацию template<typename T> struct Wrapper<T const>;? - person ildjarn; 13.07.2012
comment
@ildjarn Спасибо за это, я не думал о частичной специализации шаблонов. Используя этот подход, мне удалось получить что-то, что работает примерно так же, как и раньше, но, надеюсь, этот подход позволяет избежать потенциальных нарушений строгого алиасинга/и т. д. которые были там раньше: gist.github.com/3106817 - person HostileFork says dont trust SE; 13.07.2012
comment
@HostileFork: Строго говоря, const_cast<Foo *>(&foo) (строка 100) может вызывать UB (хотя в вашем конкретном примере это не так), а template<typename _Tp> friend struct std::default_delete; (строка 23) не переносима, поскольку реализация стандартной библиотеки бесплатна. чтобы добавить дополнительные аргументы шаблона, если они установлены по умолчанию. - person ildjarn; 13.07.2012
comment
@ildjarn Спасибо за заметки. Проблема std::default delete будет решена, как только я получу указатели Foo полностью из опубликованного интерфейса и всегда буду проходить через оболочки, поэтому я могу это исключить. Я не совсем уверен, как обойти const_cast без двух отдельных членов-указателей. Но, возможно, сделанная мной настройка уменьшит риск, заставив оболочку const использовать метод getAccessor(), который всегда возвращает const Accessor, даже если переменная-член не является константой...? gist.github.com/3106817 - person HostileFork says dont trust SE; 14.07.2012
comment
@ildjarn На самом деле, он становится еще чище (и более разумным), если вы получаете частичную специализацию шаблона. Я удивлен, что компилятор (G++ 4.7) это понимает, но похоже. gist.github.com/3106817 - person HostileFork says dont trust SE; 14.07.2012