«Это не то же самое, что» - это шаблон, который вы используете при написании конструкторов, которые идеально конвертируют - вы не хотите использовать этот конвертер, когда переданный тип является некоторым вариантом вашего собственного типа. Скорее всего, он был включен здесь копией-макаронами.
Действительно, вы хотите использовать признак «это может быть присвоено строке»: std::enable_if_t<std::is_assignable<std::string, String>::value>>
, так как это то, о чем вы заботитесь. Вы можете пойти дальше и проверить, назначено ли это (если это так, используйте это), и если это будет конвертируемо (и если это так, конвертировать, а затем назначить), но я бы этого не сделал.
Короче говоря, состояние выглядит так, как будто оно происходит из копий-макарон соответствующего теста. Вы действительно не хотите сильно его ограничивать.
Что касается того, почему он опровергает вариант №2, если std::string
в вашем контейнере уже выделил память, он может скопировать из char const*
, не выделяя больше. Если вместо этого вы берете string&&
, то char const*
сначала преобразуется в string
, то это назначается переносом. У нас есть две строки, и одна из них отбрасывается.
Что вы видите, накладные расходы памяти.
Совершенная пересылка не должна выделять память.
Теперь, в интересах полного, существует еще один вариант. Это немного сумасшествие для реализации, но оно почти так же эффективно, как и вариант №4, и имеет несколько недостатков.
Вариант 5: назначение стирания типа. assignment_view<std::string>
.
Напишите класс, который стирает «присвоение типа T
». Возьмите его как свой аргумент. Используйте его внутри.
Это более обучаемая, чем идеальная пересылка. Метод может быть виртуальным, поскольку мы берем конкретный тип (конкретный тип назначения строки). Стирание типа происходит во время построения цедента. Некоторый код генерируется для каждого назначенного типа, но код ограничивается только назначением, а не всем телом функции.
Есть некоторые накладные расходы (похожие на вызов виртуальной функции, в основном из-за сбоев кэша команд) при каждом присваивании. Так что это не идеально.
Вы звоните a.assign_to(name)
для выполнения задания вместо name = a
для максимальной эффективности. Вы можете сделать name << std::move(a);
, если вы предпочитаете синтаксис.
Для максимальной эффективности представление стирания назначения (то, что вы хотите назвать) может использоваться только для создания одного задания: это позволяет оптимизировать семантику перемещения. Вы также можете сделать умный, который делает что-то другое на &&
и &
на основе назначения-из-за стоимости одной дополнительной служебной информации указателя функции.
here I type стереть концепцию T == ?
. Это просто требует типа, стирающего понятие T = ?
. (Я могу сделать синтаксис инициализации {}
чуть лучше с Ts&&...
CTOR к объекту типа стирания теперь:. Это была моей первой попыткой в этом)
live example типа стирает вплоть до присвоения std::string
.
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<class T>struct tag{using type=T;};
template<class...>struct types{using type=types;};
template<class T>
using block_deduction = typename tag<T>::type;
template<class F, class Sig, class T=void>
struct erase_view_op;
template<class F, class R, class...Ts, class T>
struct erase_view_op<F, R(Ts...), T>
{
using fptr = R(*)(void const*, Ts&&...);
fptr f;
void const* ptr;
private:
template<class U>
erase_view_op(U&& u, int):
f([](void const* p, Ts&&...ts)->R{
U& u = reinterpret_cast<U&>(*static_cast<std::decay_t<U>*>(const_cast<void*>(p)));
return F{}(u, std::forward<Ts>(ts)...);
}),
ptr(static_cast<void const*>(std::addressof(u)))
{}
public:
template<class U, class=std::enable_if_t< !std::is_same<std::decay_t<U>,erase_view_op>{} && (std::is_same<void,R>{} || std::is_convertible< std::result_of_t<F(U,Ts...)>, R >{}) >>
erase_view_op(U&& u):erase_view_op(std::forward<U>(u), 0){}
template<class U=T, class=std::enable_if_t< !std::is_same<U, void>{} >>
erase_view_op(block_deduction<U>&& u):erase_view_op(std::move(u), 0){}
erase_view_op(erase_view_op const&) = default;
erase_view_op(erase_view_op&&) = default;
R operator()(Ts... ts) const {
return f(ptr, std::forward<Ts>(ts)...);
}
};
struct assign_lhs_to_rhs {
template<class lhs, class rhs>
void operator()(lhs&& l, rhs& r)const {
r = std::forward<lhs>(l);
}
};
template<class T>
using erase_assignment_to = erase_view_op< assign_lhs_to_rhs, void(T&), T >;
using string_assign_to = erase_assignment_to<std::string>;
это, как уже отмечалось, весьма похож на тип стирания вплоть до ==
. Я сделал некоторые скромные улучшения (void
тип возврата). Идеальная пересылка (до T{}
) ctor была бы лучше, чем block_deduction<U>&&
одна (как вы получите {}
вместо {{}}
).
'std :: string' имеет оператор присваивания, который принимает' char const * '. Этот оператор не должен выделять память, если строка 'std :: string' на LHS уже имеет достаточное пространство. – dyp
@ dyp, который применяется также к копированию из 'std :: string'. Кроме того, # 4 не может принимать строковые литералы. – bmanga
Все остальные варианты (но идеальная пересылка) требуют, чтобы вызывающий абонент преобразовывал в 'std :: string' из-за типа параметра. Если аргументом является 'char const *', для этого требуется выделение памяти, даже если созданная 'std :: string' (параметр функции) может быть скопирована без дополнительных распределений на' name_'. Что касается опции №4, то в скобках есть некоторые ошибки. Прекрасная функция пересылки будет использовать 'std :: is_convertible'. –
dyp