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

Как написать шаблон `is_complete`?

Отвечая на этот вопрос, я пытался найти шаблон is_complete в библиотеке Boost, и я понял, что такого шаблона в Boost.TypeTraits нет. Почему в библиотеке Boost нет такого шаблона? Как это должно выглядеть?

//! Check whether type complete
template<typename T>
struct is_complete
{   
  static const bool value = ( sizeof(T) > 0 );
};

...

// so I could use it in such a way
BOOST_STATIC_ASSERT( boost::is_complete<T>::value );

Вышеприведенный код неверен, потому что недействительно применять sizeof к неполному типу. Что будет хорошим решением? Можно ли как-то применить SFINAE?


Ну, эта проблема не может быть решена вообще, не нарушая правило ODR, но существует определенная платформа , который работает для меня.

4b9b3361

Ответ 1

Ответ, данный Алексеем Малистовым, можно использовать на MSVC с незначительной модификацией:

namespace 
{
    template<class T, int discriminator>
    struct is_complete {  
      static T & getT();   
      static char (& pass(T))[2]; 
      static char pass(...);   
      static const bool value = sizeof(pass(getT()))==2;
    };
}
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value

К сожалению, предопределенный макрос __COUNTER__ не является частью стандарта, поэтому он не будет работать на каждом компиляторе.

Ответ 2

template<class T>
struct is_complete {
    static T & getT();
    static char (& pass(T))[2];
    static char pass(...);

    static const bool value = sizeof(pass(getT()))==2;
};

Ответ 3

Возможно, это немного поздно, но до сих пор решение С++ 11 не работало как для полных, так и для абстрактных типов.

Итак, вы здесь.

С VS2015 (v140), g++ >= 4.8.1, clang >= 3.4, это работает:

template <class T, class = void>
struct IsComplete : std::false_type
{};

template <class T>
struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type
{};

Спасибо Bat-Ulzii Luvsanbat: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/

С VS2013 (V120):

namespace Details
{

    template <class T>
    struct IsComplete
    {
        typedef char no;
        struct yes { char dummy[2]; };

        template <class U, class = decltype(sizeof(std::declval< U >())) >
        static yes check(U*);

        template <class U>
        static no check(...);

        static const bool value = sizeof(check< T >(nullptr)) == sizeof(yes);
    };

} // namespace Details


template <class T>
struct IsComplete : std::integral_constant< bool, Details::IsComplete< T >::value >
{};

Это вдохновлено на создание сетей и static утверждают, что имя шаблона T не завершено?

Ответ 4

Я боюсь, что вы не можете реализовать такие черты типа is_complete. Реализация, данная @Alexey, не скомпилирована в g++ 4.4.2 и g++ 4.5.0:

error: инициализация аргумента 1 из 'static char (& is_complete:: pass (T)) [2] [с T = Foo]

На моем Mac с g++ 4.0.1 оценка is_complete<Foo>::value, где struct Foo; является неполной, дает значение true, что даже хуже ошибки компилятора.

T может быть как полным, так и неполным в той же программе, в зависимости от единицы перевода, но он всегда одного и того же типа. Как следствие, как отмечалось выше, is_complete<T> всегда является одним и тем же типом.

Итак, если вы уважаете ODR, то нельзя is_complete<T> оценивать разные значения в зависимости от того, где они используются; в противном случае это означало бы, что у вас есть разные определения для is_complete<T>, которые запрещают ODR.

EDIT: в качестве принятого ответа я сам взломал решение, которое использует макрос __COUNTER__ для создания другого типа is_complete<T, int> каждый раз, когда используется макрос is_complete. Однако с gcc я не мог заставить SFINAE работать в первую очередь.

Ответ 5

Для решения этой задачи требуется выполнить вычисление в аргументе по умолчанию шаблона признака, поскольку попытка изменить определение шаблона нарушает правило ODR (хотя комбинация __COUNTER__ и namespace {} может обходить ODR).

Это написано на С++ 11, но может быть настроено для работы в режиме С++ 03 умеренно недавнего компилятора, совместимого с С++ 11.

template< typename t >
typename std::enable_if< sizeof (t), std::true_type >::type
is_complete_fn( t * );

std::false_type is_complete_fn( ... );

template< typename t, bool value = decltype( is_complete_fn( (t *) nullptr ) )::value >
struct is_complete : std::integral_constant< bool, value > {};

онлайн-демонстрация

Аргумент по умолчанию оценивается там, где указан шаблон, поэтому он может контекстно переключаться между различными определениями. При каждом использовании нет необходимости в различной специализации и определении; вам нужен только один для true и один для false.

Правило приведено в п. 8.3.6/9, которое в равной степени относится к аргументам по умолчанию по умолчанию и аргументам шаблона по умолчанию:

Аргументы по умолчанию оцениваются каждый раз, когда вызывается функция.

Но будьте осторожны, использование этого внутри шаблона почти наверняка нарушит ODR. Шаблон, созданный на неполном типе, не должен делать ничего иначе, как если бы он был создан на полном типе. Я лично хочу только этого для static_assert.

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

Ответ 6

Просто ответьте, что ответ (не is_complete<T> мной) на несвязанный вопрос дает решение для is_complete<T>.

Ответ здесь. Я не вставляю это ниже, чтобы не по ошибке получить кредит на это.

Ответ 7

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

Ответ 8

Мои 5 центов:

  template <typename T, typename = void>
  struct is_incomplete : ::std::true_type
  {
  };

  template <typename T>
  struct is_incomplete<T, decltype(sizeof(T))> : ::std::false_type
  {
  };

  template <>
  struct is_incomplete<void> : ::std::false_type
  {
  };