от unique_ptr‹char[]› до unique_ptr‹const char[]›

Какая идиома лучше всего подходит для перемещения unique_ptr<char[]> в unique_ptr<const char[]>?

Пример использования: предположим, вы создаете строку C в каком-то буфере. Чтобы обеспечить надлежащую очистку в случае исключения, этот буфер может быть ссылкой с использованием unique_ptr. После создания строки вы можете захотеть переместить ее в какой-нибудь член класса, объявленный unique_ptr<const char[]>, чтобы избежать дальнейшей модификации строки.

Вот лучшее, что у меня есть:

std::unique_ptr<const char[]> res;
std::unique_ptr<char[]> buf(new char[4]);
buf[0] = 'f';
buf[1] = buf[2] = 'o';
buf[3] = '\0';
res = std::unique_ptr<const char[]>(const_cast<const char*>(buf.release()));

Простое перемещение не работает, вероятно, из-за разных типов удаления. Даже пропуск явного приведения от char к const char не работает, как обсуждалось в Если `unique_ptr‹ T const [] ›` принимает `T *` аргумент конструктора?

Есть ли лучшая идиома для достижения того, что я изложил выше? Мой код выглядит довольно неуклюжим и довольно длинным, учитывая, насколько простой концепция звучит на первый взгляд.


person MvG    schedule 14.02.2013    source источник
comment
Использование только res = std::move(buf); отлично компилируется для меня в VS2010 и VS2012 и стволе Clang (с стволом libc++). Однако GCC 4.7.2 задыхается от этого.   -  person Xeo    schedule 14.02.2013
comment
Кажется, магистраль GCC 4.8 решила проблему, и вы также можете использовать res = std::move(buf) там. В противном случае, я не думаю, что вы сможете обойти эту штуку с актерами. Однако вы можете сократить строку до res.reset(const_cast<char const*>(buf.release()));.   -  person Xeo    schedule 14.02.2013
comment
@Xeo, как информация о GCC 4.8, так и более короткая идиома с использованием reset очень ценны. Пожалуйста, опубликуйте их как ответ.   -  person MvG    schedule 14.02.2013
comment
Я знаю один лучший способ: использовать std::string. Просто не копируйте его, и он будет двигаться нормально.   -  person Nicol Bolas    schedule 14.02.2013
comment
@NicolBolas: я собираю возвращаемое значение для переопределения std::exception::what(), которое должно быть const char*. Я проделал большую часть работы над std::string, поэтому buf — это массив символов, которому я назначаю, а res — это элемент, который я использую для сохранения результата до тех пор, пока само исключение не будет собрано. Думаю, вместо этого я мог бы вернуть результат std::string::c_str(). Однако невозможно заставить класс не манипулировать этой строкой, поскольку нет std::string, которому можно было бы присвоить, но нельзя изменить на месте. Что делает всю мою проблему здесь неприменимой.   -  person MvG    schedule 14.02.2013


Ответы (2)


Стандартная библиотека VS2010 и VS2012 допускает преобразование с помощью res = std::move(buf) в качестве расширения и в ответ на DR2118. То же самое касается libc++ и libstdc++ GCC 4.8.

Для GCC 4.7 вы не обойдете const_cast, но, по крайней мере, вы можете несколько сократить строку:

res.reset(const_cast<char const*>(buf.release()));
person Xeo    schedule 14.02.2013

Библиотека Smart Ptr для Boost имеет общие функции приведения указателей, которые работают с интеллектуальными указателями из этой библиотеки, а также с указателями из STL. В частности, const_pointer_cast может сделать то, что вам нужно:

#include <boost/pointer_cast.hpp>

// ...
res = boost::const_pointer_cast<const char[]>(std::move(buf));
person ingomueller.net    schedule 02.01.2020