2016-02-21 1 views
4

Мой текущий вопрос был вдохновлен, пытаясь понять, как std::unique_ptr<T, D> использует механику шаблонов для создания экземпляра шаблона класса размер T* при D (тип Deleter) тип лямбда-функция, но большего размера, когда D является указателем на функцию type (так как пространство должно быть выделено в экземпляре unique_ptr для хранения указателя функции).Как std :: is_empty <T> реализован в VS2015 (или любом компиляторе)?

Глядя через исходный код VS2015, я считаю, что std::unique_ptr происходит от std::_Unique_ptr_base, который, в свою очередь, заявляет член данных типа _Compressed_pair<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value>. В этом последнем контексте тип _Ty1 является типом дебетера, D, то есть вторым параметром шаблона unique_ptr, отмеченным в предыдущем абзаце; то есть мотивация, стоящая за этим вопросом, заключается в том, что я сопоставляю _Ty1 как лямбда-тип, а _Ty1 - тип указателя функции. (На самом деле, значение по умолчанию bool в настоящее время используются.)

Я понимаю, что is_empty<_Ty1>::value является true, когда _Ty1 является экземпляром типа лямбды (при лямбда не имеет переменный захват и, следовательно, имеет 0 размера); но это false, когда _Ty1 - тип указателя функции.

Это привело меня к тому, чтобы определить, как определяется std::is_empty.

Ugh!

Ниже приведена полная реализация std::is_empty, которую я могу найти в исходном коде библиотеки VS2015 C++.

В файле type_traits это:

// TEMPLATE CLASS is_empty 
template<class _Ty> 
struct is_empty _IS_EMPTY(_Ty) 
{ // determine whether _Ty is an empty class 
}; 

... и макро _IS_EMPTY определяется в том же файле:

#define _IS_EMPTY(_Ty) \ 
: _Cat_base<__is_empty(_Ty)> 

... и в этот момент моя удача иссякает , потому что я не могу найти определение __is_empty в любом месте. У меня есть GREPped через весь каталог установки VS2015 (который включает, я думаю, весь исходный код библиотеки C++, хотя, возможно, я ошибаюсь).

Мне нравится понимать внутренние элементы C++, когда захочу. Но ... Я застрял на этом, и много поисковых запросов не выявило ответа (хотя я видел ссылку на intrinsics), и мое копание не ... обнаружило какой-либо исходный код.

Может кто-нибудь просветить эту ситуацию? Как работает std::is_empty<T> в VS2015 или, если на то пошло, какой-либо другой компилятор?

+0

Я не уверен в этом, но в соответствии со стандартом пустой класс имеет размер 1 байт. можно измерить размер класса и определить. Я действительно не пытался это реализовать –

+0

@DavidHaim - Это рассуждение связано с ответом, который предоставляет Dietmar Kühl. В примере в его ответе он использует тот факт, что, когда непустой класс проистекает из пустого класса, пустой объект subobject * does * имеет размер 0 - это только тогда, когда класс полностью пуст, включая все базовые class subobjects, что класс должен иметь ненулевой размер. –

ответ

6

Похоже, что MSVC++ содержит встроенный __isempty(T) вместо того, чтобы иметь дело с реализацией на уровне библиотеки. Поскольку аргумент типа T, переданный в std::is_empty<T>, может быть final, я не думаю, что может быть реализована безопасная библиотека, и может понадобиться помощь компилятора.

Единственный способ определить, является ли тип T пуст в библиотеке я могу думать, это (специализация имеет дело с не- class типов, для которых std::is_empty<T> не является true):

template <bool, typename T> 
struct is_empty_aux: T { unsigned long long dummy; }; 
template <typename T> 
struct is_empty_aux<false, T> { unsigned long long dummy[2]; }; 

template <typename T> 
struct is_empty: 
    std::integral_constant<bool, 
          sizeof(is_empty_aux<std::is_class<T>::value, T>) 
          == sizeof(unsigned long long)> { 
}; 

Однако, если T является final Наследование в is_empty_aux является незаконным. Хотя случай класса final может быть обнаружен с помощью std::is_final<T>, я не вижу способа определить, пусты ли его объекты. Таким образом, может потребоваться использование встроенного компилятора. В любом случае, свойства компилятора обязательно необходимы для некоторых черт другого типа. Интрификаторы компилятора - это декларации/определения, каким-то образом магически предоставленные компилятором: они обычно не объявляются и не определяются явно. У компилятора есть необходимые знания о типах, и разоблачение этих знаний с помощью встроенных средств является разумным подходом.

+0

Спасибо за это! Однако один из аспектов механизма шаблона, который я пытаюсь понять, заключается в том, как 'std :: is_empty ' также может успешно обрабатывать неклассовые типы, такие как 'int' или' void (*)() '. То есть, 'std :: is_empty ' и 'std :: is_empty ' оба действительны. Однако ваш небольшой пример кода не будет компилироваться, если 'T' является указателем' int' или function. Я знаю, что это связано с основным моментом, который вы сделали в своем ответе, но я также пытаюсь понять этот аспект механики шаблонов. Как вы могли бы включить в этот ответ типы non-class для 'T'? –

+0

@DanNissenbaum: Обратите внимание, что лямбда без захвата * не * функция и/или указатель функции: это все еще тип класса, но тот, который может преобразовать в указатель функции. –

+0

Это также ломается, если класс имеет достаточно большое число субобъектов с базовым классом того же типа. например, заданной 'struct X {}; template struct Y: X {}; template struct Z: Y ... {}; ',' Z <1,2,3,4,5,6,7,8,9,10> '. –

1

Также столкнулся с этим вопросом и взглянул на заголовки gcc 4.8 в Ubuntu 14.04. На самом деле есть семья из них, как __is_empty, __is_pod, __is_polymorphic и т.д., используется таким образом

// /usr/include/c++/4.8/type_traits:516 
template<typename _Tp> 
struct is_empty 
    : public integral_constant<bool, __is_empty(_Tp)> 
{}; 

Они, кажется, работают без включения каких-либо заголовков C++. Я пробовал г ++ и лязг ++ скомпилировать этот код

#include <stdio.h> 

struct X {}; 

int main() 
{ 
    printf("%d %d %d\n", __is_empty(X), __is_enum(X), __is_class(X)); 
    return 0; 
} 

То, что я чувствую себя необычно, что они похожи на функции, но на самом деле взять тип в качестве аргумента, вместо экземпляра (не работает, если вы пытаетесь X x; __is_empty(x);).