Рефакторинг метод, общий для всех классов

У меня есть глобальная функция, которая копирует соответствующие биты одного объекта (или типа Source) к другому (типа Target), вот так:

template<typename Source , typename Target>
void partialCopy( Source& source , Target& target )
{
// perform copy
}

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

struct Foo
{
template<typename T>
void partialCopy( T& target )
{
::partialCopy( *this , target );
}
};

Проблема в том, что функция-член должна быть скопирована в десятки классов. Это терпимый случай программирование копирования и вставки? Я подумал над тем, чтобы положить partialCopy в заголовочном файле partialCopy.h и используя препроцессор include, чтобы «внедрить» его в каждый класс, вот так:

struct Foo
{
#include "partialCopy.h"};Foo f;
Bar b;
f.partialCopy( b );

Хотя это работает, я никогда не видел, чтобы это было сделано где-либо, и не знаю, является ли это неприемлемым.

Я уже пытался положить partialCopy функция-член в общем базовом классе и наследовать его, но это не работает, потому что this Затем ключевое слово будет ссылаться на базовый класс, а не на производный класс.

Есть ли еще лучшая альтернатива? Пожалуйста, порекомендуйте.

редактировать

Предложение Джона (в ветке, которая была удалена), что я выполняю static_cast к производному классу в базовом классе CRTP работает хорошо. @ Джон, пожалуйста, оставьте этот ответ, и я отмечу его как таковой.

2

Решение

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

Const ссылка

использование const& (const-ссылка) для параметра источника. Таким образом, его легко отличить от цели.

Дополнительным преимуществом является то, что он будет проверять и обеспечивать правильность вашей функции частичного копирования.

Rvalue ссылка

Вы также можете подумать о его перегрузке для Source&&, Если есть некоторые буферы, которые копируются напрямую, ваша функция может использовать их.

5

Другие решения

Я бы предложил перегрузить потоковые операторы для этого.

Например.

template<typename Source , typename Target>
void partialCopy(Source& source, Target& target)
{
// perform copy
}

эффективно становится:

template<typename Source , typename Target>
void operator>>(const Source& source, Target& target)
{
// perform copy
}

(также обратите внимание, что параметр Source теперь является постоянным&, для ясности.

Так что вы могли бы просто написать

Foo f;
Bar b;
f >> b;

Делает намного более понятным, каковы исходные и целевые объекты.

3

Я немного опоздал с этим ответом, но я подумал, что вас может заинтересовать решение с использованием CRTP как чистая альтернатива программированию копирования-вставки:

Проблема в том, что функция-член должна быть скопирована в десятки классов. Является ли это допустимым случаем копирования и вставки программирования? Я подумал о том, чтобы поместить частичную копию в заголовочный файл частичный копируемый файл и использовать препроцессор включения, чтобы «внедрить» его в каждый класс […].

Вместо того, чтобы копировать или включать код, учтите следующее:

// common code:
<template typename T>
class PartialCopyImplementer
{
public:
template<typename D>
void partialCopy(D& destination)
{
// ::partialCopy( *this , target );
}
};

// concrete implementations
class Foo1 : public PartialCopyImplementer<Foo1> // CRTP implementation
{
// ...
};

// concrete implementations
class Foo2 : public PartialCopyImplementer<Foo2> // CRTP ensures Foo1 and Foo2
// do not have a common base
{
// ...
};
1

Самый чистый способ, вероятно, просто уйти partialCopy в качестве бесплатной функции использовать его таким образом. В этом нет ничего плохого, например, все функции в стандартных библиотеках <algorithm> заголовок являются бесплатной функцией, которая будет использоваться с объектами.

Также не очень понятно, какой из foo.partialCopy(bar) это источник и какой пункт назначения. Есть ли partialCopy копия от или же в bar? Обычно в таких случаях полезно взглянуть на объявление документации / функции. Если у вас есть четкие имена для параметров и сделайте их const когда это присваивается, то должно быть довольно ясно, каким образом копируются объекты.

0

Как насчет:

template<class T>
struct from_impl
{
T const& from;
from_impl(T const& f)
: from(f)
{}
};

template<class T>
from_impl<T> from(T const& f) {
return from_impl(f);
}

template<class T>
struct to_impl
{
T& to;
to_impl(T& t)
: to(t)
{}
};

template<class T>
to_impl<T> to(T& t) {
return to_impl(t);
}
template<class T>
void to(T const&); // prevent using  with non-const values

template<class Source,class Target>
void partial_copy(from_impl<Source> source, to_impl<Target> target)
{
// use source.from and target.to to perform copy
}

// usage:
T1 object1;
T2 object2;

partial_copy(from(object1),to(object2));

Это очень ясно показывает, что вы хотите сделать. from_impl и to_impl работают как своего рода ссылка, а from и работают как фабричные функции для удобства использования. Вы также можете попробовать реализовать что-то вроде.

partial_copy.from(Source).to(Target);
partial_copy(Source)->_(Target);

Но обычно это много написано. Просто поместите частичную_копию в свое собственное пространство имен, чтобы предотвратить столкновение имен, позволить пользователям создавать собственные перегрузки для настройки и использовать const.& для сигнализации, что источник и пункт назначения.

0
По вопросам рекламы [email protected]