2010-01-17 2 views
3

У меня есть дилемма. Предположим, у меня есть класс шаблона:Параметры шаблона dilemma

template <typename ValueT> 
class Array 
{ 
public: 
    typedef ValueT ValueType; 
    ValueType& GetValue() 
    { 
     ... 
    } 
}; 

Теперь я хочу, чтобы определить функцию, которая получает ссылку на класс и вызывает функцию GetValue(). Я обычно рассматривают следующие два способа:

Метод 1:

template <typename ValueType> 
void DoGetValue(Array<ValueType>& arr) 
{ 
    ValueType value = arr.GetValue(); 
    ... 
} 

Метод 2:

template <typename ArrayType> 
void DoGetValue(ArrayType& arr) 
{ 
    typename ArrayType::ValueType value = arr.GetValue(); 
    ... 
} 

Там нет почти никакой разницы между этими двумя методами. Даже вызов обеих функций будет выглядеть точно так же:

int main() 
{ 
    Array<int> arr; 
    DoGetValue(arr); 
} 

Теперь, какая из двух является лучшей? Я могу думать о некоторых минусах и плюсах:

Метод 1 плюсы:

Параметр реальный класс не шаблон, так что легче для пользователя, чтобы понять интерфейс - это очень четко указано, что параметр должен быть массивом. В методе 2 вы можете угадать это только от имени. Мы используем ValueType в функции, поэтому это более ясно, чем когда оно скрыто внутри массива и должно быть доступно с помощью оператора области.

Кроме того, ключевое слово typename может вводить в заблуждение для многих программистов, не имеющих шаблонов.

Метод 2 плюсы:

Эта функция является более "истинным" к своей цели. Когда я думаю, что если это так, мне не нужен класс, чтобы быть Array. Мне действительно нужен класс, который имеет метод GetValue и тип ValueType. Это все. То есть этот метод является более общим.

Этот метод также меньше зависит от изменений в классе Array. Что делать, если параметры шаблона массива изменены? Почему это должно повлиять на DoGetValue? На самом деле неважно, как определяется массив.

Evey time У меня такая ситуация Я не уверен, что выбрать. Каков твой выбор?

+3

В целях обеспечения целостности я бы также рассмотрел возможность «изменения» интерфейса вашего собственного класса и попытаться имитировать контейнеры STL, таким образом вы сможете использовать свой собственный шаблон функций в контейнерах STL в качестве бонуса> 'value_type 'вместо' ValueType', 'front' или' back' вместо 'GetValue' (или, возможно,' at', если он ожидает позицию?) и т. д. –

+0

@Matthieu, хороший момент! Это то, что я должен рассмотреть. Я никогда не думал о возможности мистификации STL. Это действительно полезно и даже дает визуальные подсказки в коде. Благодарю. – FireAphis

ответ

1

Если ваша функция очень специфична для ArrayType, и никакого другого шаблона не будет удовлетворять свои потребности интерфейса, используйте # 1, как это и короче и более конкретно: случайный читатель узнает, что она работает на ArrayType.

Если есть вероятность, что другие шаблоны будут совместимы с DoGetValue, используйте # 2, поскольку это более общий.

Но бесполезно использовать, потому что легко конвертировать между ними.

+0

Мне нравится «бесполезная одержимая» часть :) – FireAphis

4

Второй лучше. Вы говорите: «В ваших« плюсах »для первого вы говорите:« Очень ясно, что параметр должен быть массивом ». Но сказать, что параметр должен быть массивом, является ненужным ограничением. Во втором примере будет выполняться любой класс с подходящей функцией GetValue. Поскольку это ненужное ограничение, лучше удалить его (второе), чем сделать его явным (первым). Вы напишете более гибкие шаблоны, которые полезны в будущем, когда вы хотите получить значение от чего-то, что не является массивом.

+1

В общем, я согласен, я сам склонен идти со вторым вариантом, но в закрытой экосистеме, где я действительно хочу использовать использование массива, это ограничение может стать преимуществом. – FireAphis

+1

Да, возможно, я должен добавить некоторую двусмысленность - когда я говорю «лучше», я имею в виду, что это первый выбор, если не существует определенного фактора, не указанного в вопросе, что делает что-то еще лучше в конкретном случае. Например, если вы используете 15 различных функций Array и вы используете интерфейс Array для значительного изменения во время разработки, а для DoGetValue всегда должны быть изменены, чтобы соответствовать, а затем позволить людям использовать DoGetValue с другим классом, значит, им придется постоянно менять другой класс тоже, что, вероятно, хуже, чем использование массива. –

+0

@FireAphis, если вы хотите ограничить Array (я могу себе представить, что уменьшение вероятности двусмысленности является хорошей причиной, когда то, что вы перегружаете, является оператором), то почему бы не использовать лучшее из двух миров: Accept Array , но прежнему использовать :: ValueType –

0

Мой друг предложил еще два, несколько более экстремальные, методы:

Метод 3: дает возможность использовать типы, которые не имеют :: ValueType.

template <typename ArrayType, typename ValueType = ArrayType::ValueType> 
void DoGetValue(ArrayType& arr) 
{ 
    ValueType value = arr.GetValue(); 
    ... 
} 

Метод 4: классный способ заставить массив быть классом, который имеет один параметр шаблона.

template <template <typename> class ArrayType, typename ValueType> 
void DoGetValue(ArrayType<ValueType>& arr) 
{ 
    ValueType value = arr.GetValue(); 
    ... 
} 
+3

Метод 3 требует 'typename ArrayType :: ValueType' – UncleBens

+0

Почему вы хотите ограничить количество параметров? Похоже, стрелять в ногу. – Potatoswatter

+0

@Potatoswatter, вы правы, конечно. Эти решения здесь больше из академического интереса :) – FireAphis

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