Важность специализации шаблона функции и необходимость

Я прочитал Краткую информацию C++, и она говорит, что специализация шаблона функции является усовершенствованной темой, но я полностью потерян. Кто-либо может предложить пример, почему специализация шаблона функции важна и необходима?

Почему шаблоны функций не поддерживают частичную специализацию, в то время как шаблоны классов делают? Какова базовая логика?

6
задан skydoor 5 February 2010 в 04:15
поделиться

4 ответа

По сути, идея состоит в том, что вы можете писать шаблоны, которые ведут себя обычным образом для общего случая, но могут обрабатывать особые случаи. Одним из примеров использования специализации является std :: vector . std :: vector - это специализация, которая упаковывает элементы bool таким образом, что они используют только один бит на элемент, а не один байт. std :: vector работает как обычный динамический массив для всех других типов.

Более сложное использование специализации - метапрограммирование. Например, вот пример (из Википедии) того, как использовать специализацию шаблонов для вычисления факториалов во время компиляции.

template <int N>
struct Factorial 
{
    enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};
0
ответ дан 9 December 2019 в 20:43
поделиться

Ваш На вопрос, почему функции не поддерживают частичную специализацию, можно ответить здесь . В приведенном ниже коде показано, как реализовать различные специализации.

template<typename T>
bool Less(T a, T b)
{
    cout << "version 1 ";
    return a < b;
}
// Function templates can't be partially specialized they can overload instead.
template<typename T>
bool Less(T* a, T* b)
{
    cout << "version 2 ";
    return *a < *b;
}

template<>
bool Less<>(const char* lhs, const char* rhs)
{
    cout << "version 3 ";
    return strcmp(lhs, rhs) < 0;
}

int a = 5, b = 6;

cout << Less<int>(a, b) << endl;
cout << Less<int>(&a, &b) << endl;
cout << Less("abc", "def") << endl;
8
ответ дан 9 December 2019 в 20:43
поделиться

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

Например, если вам нужно поменять местами два элемента, то лучше полагаться на перегрузки (более предсказуемые и более расширяемые):

template<class T>
void f() {
  T a, b;
  using std::swap; // brings std::swap into scope as "fallback"
  swap(a, b); // unqualified call (no "std::") so ADL kicks in
  // also look at boost::swap
}

И как вы пишете своп для своих типов:

// the cleanest way to do it for a class template:
template<class T>
struct Ex1 {
  friend void swap(Ex1& a, Ex1& b) { /* do stuff */ }
};

// you can certainly place it outside of the class instead---but in the
// same namespace as the class---if you have some coding convention
// against friends (which is common, but misguided, IMHO):
struct Ex2 {};
void swap(Ex2& a, Ex2& b) { /* do stuff */ }

Оба из них позволяют Зависимый от аргумента поиск (ADL).

Другие функции, такие как stringify / str или repr (представление), могут аналогичным образом не являться членами и использовать преимущества ADL посредством перегрузки:

struct Ex3 {
  friend std::string repr(Ex3 const&) { return "<Ex3 obj>"; }
};

std::string repr(bool b) { return b ? "true" : "false"; }

// possible fallback:
template<class T>
std::string repr(T const& v) {
  std::ostringstream out;
  out << v;
  return out.str();
}
// but in this particular case, I'd remove the fallback and document that
// repr() must be overloaded appropriately before it can be used with a
// particular type; for other operations a default fallback makes sense

Чтобы взглянуть на это с другой стороны, было бы неплохо, если бы шаблоны функций могли служат реестром для конкретных реализаций, но из-за ограничений (в текущем C ++, не уверен, что именно здесь предлагает C ++ 0x) они не работают так же хорошо, как перегрузка или шаблоны классов для этого реестра цель.

Есть одно использование, которое удобно, но не важно : легко определять определенные специализации в отдельной библиотеке, возможно, в общей библиотеке (.so или .dll). Это удобно, потому что требует минимальных изменений в универсальном шаблоне, но не важно, потому что мне это кажется редким (в дикой природе и, конечно, редко в моем опыте), и разработчики все еще могут использовать либо перегрузку, либо переадресацию в полностью специализированный класс. неспециализированный метод шаблона.

3
ответ дан 9 December 2019 в 20:43
поделиться

Они расположены по адресу/System/Library/Framework/OpenGL.framework/Headers. Чтобы включить их, просто используйте:

   #include <OpenGL/gl.h>
   #include <OpenGL/glu.h>
   #include <OpenGL/glext.h>
   #include <GLUT/glut.h>

и т.д. Убедитесь в наличии связи с соответствующими рамками, например,

cc <your_file.c> -framework GLUT -framework OpenGL 

для OpenGL и GLUT

-121--1607229-

Дополнительные предметы для добавления в список, без особого порядка:

  • Производительность - задержка, полоса пропускания, масштабирование или другие факторы, связанные с поставляемым продуктом
  • Поддержка - У вас уже есть журнал, но как насчет сантехники в других диагностических измерениях (Java-JMX, Windows-WMI/счетчики производительности или что-то пользовательское)
  • Гибкость - трудности при изменении архитектуры позже (обычно это одна из тех вещей, которые чрезмерно выполняются и вызывают больше проблем, чем необходимо, но обсуждение должно присутствовать при оценке дизайна)
  • Модель многопоточности/модель блокировки - Ясно ли, как данные будут защищены? Как будет обрабатываться конфликт ресурсов?
  • Требования к ресурсам - потребление памяти на различных уровнях использования. Соответствует ли он вашим ограничениям?
  • Обработка ошибок - Когда что-то в продукте выходит из строя, поддерживает ли архитектура чистую обработку и уведомление о проблемах?
  • Понятно - Слишком сложные архитектуры, как правило, забываются во время реализации. Если большинство команды, и в особенности руководители, не могут сохранить архитектуру, это философия и правила, в голове, архитектура не будет иметь значения.
  • Согласованность. Пытается ли он использовать все возможные образцы или сосредоточиться на регулярных, повторяющихся узорах. Не то чтобы выбор правильного образца для каждой проблемы не является хорошей вещью, но наличие большого количества узоров может повлиять на понятность и привести к ошибкам реализации. Архитектор должен стараться демонстрировать все, что он знает (или самую последнюю классную вещь) в каждом проекте.
  • Тестируемый - помогает ли проект тестированию (системе и интеграции). Может ли тестирование быть автоматизированным или вы будете полагаться на армию тестеров, чтобы постоянно повторять регрессионное тестирование, чтобы вы знали, что ваша команда не сломала продукт?
  • Действительно ли архитектура решает проблему, которую вы пытаетесь решить, и в рамках этой проблемы? Не создавайте скребок, когда будет достаточно дуплексного режима.
-121--4859578-

Для иллюстрации важности специализации шаблонов функций рассмотрим функцию std:: swap . По умолчанию std:: swap (x, y) по существу делает:

T temp = x;
x = y;
y = temp;

, но это может быть неэффективно, поскольку предполагает создание дополнительной копии x и может выполнять дополнительное копирование в присвоениях. Это особенно плохо, если x велик (например, если это std:: vector с множеством элементов). Кроме того, каждая из вышеперечисленных строк может завершиться сбоем и вызвать исключения, потенциально оставляя x и y в плохом, несогласованном состоянии.

Для решения этой проблемы многие классы предоставляют собственные методы swap (включая std:: vector )вместо этого следует поменять местами указатели на их внутренние данные. Это более эффективно и может быть гарантировано никогда не потерпеть неудачу.

Но теперь у вас есть случай, когда вы можете использовать std:: swap (x, y) для некоторых типов, но вам нужно вызвать x.swap (y) для других типов. Это сбивает с толку, и это плохо для шаблонов, поскольку они не смогут поменять местами два объекта общим, согласованным способом.

Но std:: swap может быть специализированным для вызова x.swap (y) при вызове определенных типов. Это означает, что вы можете использовать std:: swap везде и (надеюсь) ожидать, что он будет хорошо себя вести.

1
ответ дан 9 December 2019 в 20:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: