У меня есть глобальная функция, которая копирует соответствующие биты одного объекта (или типа 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 работает хорошо. @ Джон, пожалуйста, оставьте этот ответ, и я отмечу его как таковой.
Я публикую это как ответ, потому что, на мой взгляд, это уместно. Хенрик прокомментировал первым, хотя. (Впрочем, это тоже была моя первая мысль :))
использование const&
(const-ссылка) для параметра источника. Таким образом, его легко отличить от цели.
Дополнительным преимуществом является то, что он будет проверять и обеспечивать правильность вашей функции частичного копирования.
Вы также можете подумать о его перегрузке для Source&&
, Если есть некоторые буферы, которые копируются напрямую, ваша функция может использовать их.
Я бы предложил перегрузить потоковые операторы для этого.
Например.
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;
Делает намного более понятным, каковы исходные и целевые объекты.
Я немного опоздал с этим ответом, но я подумал, что вас может заинтересовать решение с использованием 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
{
// ...
};
Самый чистый способ, вероятно, просто уйти partialCopy
в качестве бесплатной функции использовать его таким образом. В этом нет ничего плохого, например, все функции в стандартных библиотеках <algorithm>
заголовок являются бесплатной функцией, которая будет использоваться с объектами.
Также не очень понятно, какой из foo.partialCopy(bar)
это источник и какой пункт назначения. Есть ли partialCopy
копия от или же в bar
? Обычно в таких случаях полезно взглянуть на объявление документации / функции. Если у вас есть четкие имена для параметров и сделайте их const
когда это присваивается, то должно быть довольно ясно, каким образом копируются объекты.
Как насчет:
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.& для сигнализации, что источник и пункт назначения.