В обзоре кода мы с коллегой обсуждали интерфейс для функции, которую я писал. Наша база кода использует C ++ 17, и мы не используем исключения (видеоигры).
Я утверждал, что способ получения параметров приемника idiomati c C ++ будет производительным при сохранении гибкости интерфейса, позволяя вызывающая сторона для передачи копии или перехода от собственного значения по желанию. Под идиоматическим способом c я подразумеваю либо одну функцию, принимающую параметр по значению, либо набор overoad для ссылок const lvalue и rvalue (что требует одного меньшего перемещения в случае lvalue за счет некоторого дублирования кода).
struct A {};
class ByValue
{
public:
ByValue(std::vector<A> v)
: m_v(std::move(v))
{}
private:
std::vector<A> m_v;
};
class RefOverloads
{
public:
RefOverloads(std::vector<A> const& v)
: m_v(v)
{}
RefOverloads(std::vector<A>&& v)
: m_v(std::move(v))
{}
private:
std::vector<A> m_v;
};
int main()
{
std::vector<A> v0;
ByValue value0(v0);
ByValue value1(std::move(v0));
std::vector<A> v1;
RefOverloads ref0(v1);
RefOverloads ref1(std::move(v1));
}
С другой стороны, моему коллеге не нравится, что легко неявно делать дорогие копии. Он предпочел бы, чтобы эти аргументы приемника всегда были по ссылке rvalue (без перегрузки const lvalue ref), и если вызывающий объект хочет передать копию, он должен сделать локальную копию и переместить ее в функцию.
class RvalueRefOnly
{
public:
RvalueRefOnly(std::vector<A>&& v)
: m_v(std::move(v))
{}
private:
std::vector<A> m_v;
};
int main()
{
std::vector<A> v;
//RvalueRefOnly failedCopy(v); // Fails purposefully.
std::vector<A> vCopy = v; // Explicit copy of v.
RvalueRefOnly okCopy(std::move(vCopy)); // Move into okCopy.
}
Я никогда даже не думал о таком интерфейсе. У меня был контраргумент: взятие по значению лучше выражает намерение, т. Е. С подписью
void f(T x);
вызывающий знает, что f
стал владельцем x
. С
void g(T&& x);
g
может иметь право собственности или нет, в зависимости от реализации f
.
Есть ли лучший способ? Я пропускаю какой-то аргумент, так или иначе?