Вопрос

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

Это было полезно?

Решение

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

Другие советы

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;
};

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

Благодаря Бат-Ульзии Лувсанбату: 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 не является полным?

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

  

ошибка: инициализация аргумента 1 из & # 8216; статический символ (& amp; is_complete :: pass (T)) [2] [with T = Foo] & # 8217;

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

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

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

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

Чтобы решить эту проблему, необходимо выполнить вычисления в аргументе шаблона признака по умолчанию, поскольку попытка изменить определение шаблона нарушает правило 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.

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

  

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top