2015-04-07 4 views
2

Я пытаюсь найти хороший способ написать функцию setter для класса шаблона. Для классов, отличных от шаблонов, если они тривиальны, потому что функция/подпись функции зависит от типа параметра. Например, если тип параметра int следующая функция должна быть оптимальной:Setters в классах шаблонов

void MyClass::Set(int value) 
{ 
    myValue = value; 
} 

Если тип параметра std::vector следующая реализация должна быть близка к оптимальной:

void MyClass::Set(std::vector<SomeType> value) 
{ 
    std::swap(myValue, value); 
} 

как правильный конструктор (перемещение или copy) будет использоваться для построения параметра функции, и не происходит ненужного копирования, если предположить, что стоимость конструктора перемещения незначительна.

Как вы можете видеть обе реализации имеют недостатки при изменении типа: Если тип изменяется на std::vector для первой версии, по крайней мере, один ненужный копия сделана увеличение фактической стоимости на коэффициент 2 или 3. Если тип изменен на int во втором варианте 2 ненужные копии сделаны, увеличивая фактическую стоимость на коэффициент 3.

Можете ли вы дать мне хорошую общую реализацию функции сеттер (возможно, с перегрузками)? Он должен быть оптимальным/близким к оптимальному для любого типа, используемого в качестве параметра.

PS: Я бы предпочел не использовать std::enable_if, чтобы сделать несколько установок, зависящих от типа, поскольку класс значительно увеличится.

+0

Вы знакомы с r значениями ссылок и/или универсальными ссылками? – AndyG

+0

Если вы не знаете, какой тип данных вы задаете, зачем даже использовать сеттер? Не похоже, что вы можете поддерживать любой инвариант класса таким образом; звучит как оправданный вариант использования 'struct', а не' class', просто оставьте член публичным и пусть пользователь назначает его оптимально. – DanielKO

ответ

6

Вы можете использовать ссылку переадресации, чтобы принять либо RValue или именующее выражение, то вперед, что к соответствующему ходу или копирующего оператора присваивания:

template <typename T> 
void Set(T && value) { 
    myValue = std::forward<T>(value); 
} 
+0

Не могли бы вы объяснить мне, как T выведено в следующих двух случаях: Set (1) и Set (std :: complex {1.0, 2.0}); – Felics

+0

@Felics: 'int &&' и 'complex &&' –

+0

Спасибо! И еще один вопрос, если вы любезны ответить мне - оптимально ли для ints передаваться как int && s? – Felics

0

Вы должны иметь 3 перегрузкам.

void Set(T&& t) { 
    v = std::move(t); 
} 
void Set(T const& t) { 
    v = t; 
} 
template<class U> 
void Set(U&& u) { 
    v = std::forward<U>(u); 
} 

первых два позволяет неявное преобразование хорошо работать, если абонент использует {} инициализации на аргументе, или если вызывающий абонент является именем функции и нуждается в контексте или несколько других случаях: это будет работать. Это фиксирует некоторые из больших неприятностей с «совершенной переадресацией».

Третий дает вам все, за что ваш operator= перегружен для обработки, который не покрывается первыми двумя. Это пример «совершенной пересылки», который, как следует из названия, несовершенен.

Самая распространенная проблема, если вы используете только 3-ю перегрузку, .Set({construction args}) не работает.

Возможно, перегрузка const& избыточна, но я не уверен.

live example


// and sometimes: 
template<class...Us> 
void Set(Us&&...us) { 
    v = {std::forward<Us>(us)...}; 
} 

Четвертый является устанавливать-множеством. В этом случае это не очень полезно (где мы используем operator=), но в некоторых случаях это может быть. Для примера, где такой приемник emplace полезен, optional::value_or должен иметь эту перегрузку.В основном, случаи, когда вы могли напрямую построить целевое значение или где другие параметры могли бы привести к вам не, построив значение, это полезно: если вы делаете {std::forward<Us>(us)...}, вы всегда можете использовать .Set({args...}) извне, что вызывает первую перегрузку выше.

Смежные вопросы