Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

Проблема

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

Например:

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. Я называю store в этом примере чрезмерно общим. std::advance и std::distance являются классическими примерами чрезмерно общего кода. Это нужно защищать. И это проблема, которую пытались исправить.