N n;
A(N n): n(std::move(n)) {}
//A(N&& n): n(std::forward<N>(n)) {} // ambiguous
};
struct B {
N n;
//B(N n): n(std::move(n)) {} // ambiguous
B(const N& n): n(n) {}
B(N&& n): n(std::forward<N>(n)) {}
};
int main() {
N n;
A a(std::move(n));
B b(std::move(n));
}
Преимущества структуры А: единственный конструктор; недостатки: двойной вызов конструктора перемещения.
Преимущества структуры B: единственный вызов конструктора перемещения; недостатки: два конструктора.
Вопрос: можно ли объединить оба преимущества в одной структуре и, если да, то как? Принимать в конструктор структуры аргументы конструктора N и создавать на месте не предлагать. :)
Можно поизвращаться с forward и SFINAE, но вопрос целесообразности очень остр
Ну вот да, хотелось бы элегантное решение, так то вместо какой-то телеги на 3 листа проще 2 конструктора написать. Как ни странно, clang любит вариант 1.
Забей болт и пиши конструктор с приёмкой по значению, мув конструктор это ничто, свап указателей по сути
Та я так и делал. Просто посетила мысль: вдруг можно лучше?
Лучше - один конструктор с forward ссылкой. Минус - неявные типы и необходимость писать тело в хэдере
И надо свиньей отрубать для левых типов
Вот после этого и посетила как раз. :) Но это, понятное дело, разные вещи.
Общее решение — использование forward ссылок, пример на C++20: struct A { N n; template<typename T> requires std::same_as<N, std::remove_cvref_t<T>> A(T&& n): n(std::forward<T>(n)) {} }; На предыдущих стандартах понадобится использовать SFINAE для ограничения типов, впрочем, переписывается практически 1 в 1, но чуть более сложночитаемо
template <utils::same_as_decayed<N> T> A::A(T&& t) : ...
Я подумал, что внесение дополнительных сущностей из utils в демонстрационном примере — плохая идея) Я бы имя упростил до like_as, кстати
Про forward ссылки понятно. Вопрос именно для невыводимых типов.
Это так, вишенка. Я сначала хотел same_as втупую вместо typename поставить, но вывод параметра все портит
Я, кажется, не смог понять, какую проблему вы имеете ввиду под невыводимыми типами, можно немного подробнее?
То, что у меня не шаблон, а просто структура. А forward -- это только про выводимые типы.
Последнее предложение для меня всё ещё остаётся неясным, если честно
А что именно не ясно? Что "универсальные" ссылки, за счёт которых это работает, существуют только в контексте выводимых типов?
Я с трудом себе представляю семантику универсальной передачи невыводимых типов Даже более того, с трудом представляю хоть какую-то передачу невыводимого типа
Как первое связано со вторым?
Так вопрос же был не про универсальную передачу. Это было ваше предложение решения. Да, я в курсе, как решить это для выводимых типов.
У вас в самом начале такая постановка вопроса: >> Преимущества структуры А: единственный конструктор >> недостатки B: два конструктора >> можно ли объединить оба преимущества в одной структуре и, если да, то как Если под единственным конструктором понимать единственное тело, ваш вопрос уже семантически эквивалентен "можно ли обеспечить универсальную передачу?"
Нет, не эквивалентен. Один конструктор + одна операция перемещения это не то же самое, что универсальная передача. Хотя универсальная передача -- один из способов решения.
Один конструктор + одна операция перемещения — это один move ctor и здесь совершенно не должно возникать каких-либо проблем: struct C { N n; C(N&& n) : n(std::move(n)) {} }; Конечно же, конструктор копирования станет недоступен в таком случае. Но в действительности, в вашем коде продемонстрировано, что вы ещё, дополнительно, хотите и copy ctor в структуре B, т.е. вам требуется именно универсальное решение
Обсуждают сегодня