Как написать шаблон `is_complete`? - PullRequest
18 голосов
/ 26 октября 2009

После ответа на этот вопрос я пытался найти шаблон 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 , но есть решение для платформы , специфичное для платформы, которое работает для меня.

Ответы [ 7 ]

13 голосов
/ 24 декабря 2009

Ответ, данный Алексеем Малистовым, можно использовать на 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 не является частью стандарта, поэтому он не будет работать на каждом компиляторе.

9 голосов
/ 12 мая 2016

Это может быть немного поздно, но пока что ни одно решение C ++ 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 >
{};

Это навеяно из интернета и статически утверждают, что имя типа T НЕ является полным?

9 голосов
/ 26 октября 2009
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;
};
2 голосов
/ 16 июля 2013

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

Это написано на C ++ 11, но может быть настроено для работы в режиме C ++ 03 умеренно недавнего C ++ 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__ с использованием шаблонов и перегрузок.

2 голосов
/ 23 декабря 2009

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

ошибка: инициализация аргумента 1 статического символа (& 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.

РЕДАКТИРОВАТЬ: Как принятый ответ, я сам взломал решение, которое использует макрос __COUNTER__ для создания экземпляра другого типа is_complete<T, int> при каждом использовании макроса IS_COMPLETE. Однако с gcc я не смог заставить SFINAE работать.

1 голос
/ 05 апреля 2018

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

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

0 голосов
/ 26 октября 2009

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

...