Для проверки времени компиляции, которая проверяет, является ли тип T
std::list
, я придумал этот шаблон:
#include <list>;
template <typename T>
struct is_std_list: std::false_type
{};
template <typename T, typename Allocator>
struct is_std_list<std::list<T, Allocator>>: std::true_type
{};
template <typename T>
inline constexpr bool is_std_list_v = is_std_list<T>::value;
int main()
{
static_assert(is_std_list_v<std::list<int>>);
return 0;
}
И мой вопрос: необходимо ли иметь #include <list>
или предварительного объявления std::list
будет достаточно? (Потому что на самом деле мне также нужно написать аналогичные шаблоны для std::vector
, std::map
, std::set
и т. д. И я боюсь, что включение всех этих заголовков потенциально может привести к увеличению времени компиляции.):
#include <memory>;
namespace std
{
template <class T, class Allocator = allocator<T>>
class list;
}
template <typename T>
struct is_std_list: std::false_type
{};
template <typename T, typename Allocator>
struct is_std_list<std::list<T, Allocator>>: std::true_type
{};
template <typename T>
inline constexpr bool is_std_list_v = is_std_list<T>::value;
int main()
{
static_assert(is_std_list_v<std::list<int>>);
return 0;
}
Направление объявления чего-либо из std
является неопределенным поведением: stackoverflow.com/questions/307343/…
Технически, предварительного объявления достаточно, но у нас нет специального заголовка с форвардным объявлением для std::list
(std::set
и т. д.), поэтому вам придется включить <list>
. Для библиотеки ввода-вывода есть заголовок <iosfwd>
, но нет для других компонентов.
Помимо того, что это запрещено стандартом, существует также проблема, заключающаяся в том, что параметры по умолчанию могут быть объявлены только один раз. Таким образом, если в предварительном объявлении распределитель задан по умолчанию, его нельзя повторить в «настоящем» объявлении. Большая проблема!
Необходимо ли иметь #include или достаточно иметь предварительное объявление std::list?
Да, вам нужно будет включить эти заголовки, если вы используете их каким-либо образом, требующим их полноты. По сути, мы платим за то, что используем.
На самом деле, предварительное объявление будет работать до тех пор, пока эти типы не обязательно будут полными, но если вы используете их так, чтобы они были полными, тогда да, вам нужно будет включить соответствующие заголовки.
В вашем примере вы не используете тип T
таким образом, чтобы требовать его полноты, но учтите, что предварительное объявление стандартного контейнера не разрешено.
Также смотрите Вперед объявить стандартный контейнер?
Не уверен, что это важно, но std::is_same
явно не указывает, что любой тип должен быть полным в столбце комментариев, в то время как все остальные признаки в таблице указаны: eel.is/c++draft/type.traits# вкладка:meta.rel
В общем, предварительного объявления достаточно, но это не вариант для стандартных классов, поскольку не существует законного способа их предварительного объявления.
К ним и namespace std
могут быть прикреплены или не быть аннотации, специфичные для компилятора, и у вас могут возникнуть или не возникнуть проблемы из-за того, что вы не дублируете эти аннотации в своих предварительных объявлениях.
Лучшее решение здесь — вместо проверки всех отдельных контейнеров — проверить их свойства (например, наличие методов .begin()
и .end()
). Возможно, std::ranges::input_range
— это то, что вам нужно, например.
Да, вам нужно будет включить эти заголовки, если вы используете их каким-либо образом, требующим их полноты. По сути, мы платим за то, что используем.