Общие / шаблонные рекомендации по программированию: ограничить типы или не ограничивать типы

Это мой вопрос. Мне просто интересно, каков консенсус в отношении ограничения типов, которые могут быть переданы в общую функцию или класс. Я думал, что читал в какой-то момент, что если вы делаете общее Программирование, обычно лучше оставить вещи открытыми, а не пытаться закрыть их (не помню источник).

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

кто-нибудь может иметь некоторые источники для статистики или авторитетных комментариев по этой теме? Меня также интересуют здравые мнения. Надеюсь, это не делает недействительным этот вопрос вообще:

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

Edit: один ответ до сих пор упоминал, что тип библиотеки, которую я делаю, будет значительным. Это библиотека баз данных, которая в конечном итоге работает с контейнерами STL, variadics (кортеж), Boost Fusion, вещами такого рода. Я понимаю, как это может быть важно, но меня также интересуют эмпирические правила для определения того, куда идти.

5 ответов


всегда оставляйте его как можно более открытым - но обязательно

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

в моей базе данных framework я решил отказаться от шаблонов и использовать один базовый класс. Общее Программирование означало, что любой или все объекты могут быть использованы. Конкретные классы типов перевешивали несколько универсальных операций. Например, строки и числа можно сравнить для равенства; BLOBs (двоичные большие объекты) могут захотеть использовать другой метод (например, Сравнение контрольных сумм MD5, хранящихся в другой записи).

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

используя иерархию наследования, я могу ссылаться на любое поле с помощью Field класс или специализированный класс, такой как Field_Int.


Это один из самых сильных пунктов продажи STL, что он настолько открыт, и что его алгоритмы работают с моими структурами данных, а также с тем, который он предоставляет сам, и что мои алгоритмы работают с его структурами данных, а также с моими.

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

(Первоначально я хотел ответить, что широко открытое-это то, что такое универсальное Программирование, но теперь я вижу, что всегда есть пределы универсальности, и что вы должны где-то провести черту. Это может быть также ограничено вашими типами, если это имеет смысл.)


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

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


Проблема

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

например:

namespace Database
{

// internal API, not documented
template <class DatabaseItem>
void
store(DatabaseItem);
{
    // ...
}

struct SomeDataBaseType {};

}  // Database

namespace ClientCode
{

template <class T, class U>
struct base
{
};

// external API, documented
template <class T, class U>
void
store(base<T, U>)
{
    // ...
}

template <class T, class U>
struct derived
    : public base<T, U>
{
};

}  // ClientCode

int main()
{
    ClientCode::derived<int, Database::SomeDataBaseType> d;
    store(d);  // intended ClientCode::store
}

в этом примере автор main даже не знает, что Database:: store существует. Он намерен вызвать ClientCode:: store и ленится, позволяя ADL выбрать функцию вместо указания ClientCode::store. В конце концов, его аргумент store происходит из того же пространства имен, что и store Так что это должно просто работать.

это не работает. Этот пример вызывает Database::store. В зависимости от внутренностей Database::store этот вызов может привести к ошибке времени компиляции, или, что еще хуже, Ошибка времени выполнения.

Как Исправить

чем более обобщенно вы называете свои функции, тем более вероятно, что это произойдет. Дайте вашим внутренним функциям (тем, которые должны отображаться в ваших заголовках) действительно не общие имена. Или поместите их в подпространство, например details. В последнем случае вы должны убедиться, что ваши клиенты никогда не будет details как связанное пространство имен для ADL. Это обычно достигается путем не создания типов, которые клиент будет использовать, прямо или косвенно, в namespace details.

если вы хотите стать более параноиком, начните блокировать вещи с enable_if.

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

приведенный выше пример не является надуманным. Это случилось со мной. Это случилось с функциями в namespace std. Я зову слишком общего. std::advance и std::distance классические примеры слишком общий код. Это то, от чего нужно остерегаться. И это проблема концепций пытались устанавливать.