2009-10-26 2 views
17

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

//! 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 rule, но есть там конкретная платформа solution, которая работает для меня.

+10

Я думаю, что это не может работайте в принципе (за исключением случаев, когда вы всегда применяете его к типу, который всегда остается неполным или всегда завершен). Поскольку 'U' завершен или нет,' is_complete 'всегда указывает тот же самый тип. Если вы сейчас перейдете и используете 'is_complete ' в двух разных единицах перевода, член значения, возможно, будет иметь различное значение каждый раз, а компилятор свободен в том, какое значение он использует. Это неправда, я думаю, но я не могу найти утверждение стандарта об этом :(Был бы рад, если бы вы могли это выяснить. –

+0

. Чем больше вопрос, почему. Без рефлексии почему вам это нужно, поскольку компилятор знает об этом время компилятора –

+0

В C++ 03 также нет способа сделать это. Atmost с C++ 0x с «sfinae для выражений», но даже тогда, если вы передаете 'vector ' например, и 'vector' объявлен только но не определено, тогда проверка полноты приведет к неявному экземпляру, и если определение не доступно, выдается жесткая ошибка, которая не покрывается sfinae (ошибка не находится в «непосредственном контексте»). –

ответ

11

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

+0

' __COUNTER__' поддерживается MSVC и gcc начиная с 4.3. Однако с gcc проблема заключается не в том, чтобы создавать разные типы 'is_complete' с помощью' __COUNTER__', но при этом компилятор делает SFINAE и выбирает 'pass (...)' перегрузку. –

+0

Это, похоже, не совместимо с полными абстрактными типами. На MSVC 2008 'IS_COMPLETE (MyAbstractClass)' не удается скомпилировать. И даже если компилятор допустил это и смог сделать SFINAE с абстрактным типом, кажется, что он даст неправильный ответ (то есть он сообщит, что полный абстрактный тип был фактически неполным). – bshields

+3

Это нарушает правило ODR. Счетчик дает вам несколько разных специализаций в одной единицы перевода, но если вы используете его во втором файле, та же специализация получит несовместимое определение. Решение состоит в том, чтобы поместить его в неназванное пространство имен. – Potatoswatter

0

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

+0

"_that, если T некорректно в какой-либо точке, но позже завершено в этой блоке перевода, что все ссылки на T относятся к одному и тому же типу_", действительно, определенный пользователем тип не является ** неотъемлемо полным или неполным **, это только неполное, пока оно не будет завершено ('void' по своей сути неполно). Поэтому вся идея чрезвычайно проблематична. – curiousguy

+1

'sizeof' плохо сформирован для неполных типов и не может дать нуль, поскольку C++ 98:« Оператор 'sizeof' не должен применять к выражению, которое имеет ... неполный тип, ... или в круглое имя такого типы «. Лечение GCC является несоответствующим, но это хорошая идея поддержать его в любом случае, если вы пишете такое средство. – Potatoswatter

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

Приятно, но, как говорит @litb в своем комментарии, он не работает должным образом, если is_complete появляется в 2 противоречащих друг другу местоположениях в том же файле, когда между ними появляется определение типа класса (я пробовал :)). – Asaf

+0

здесь с ошибкой: инициализирующий аргумент 1 «статический char» (& is_complete :: pass (T)) [2] [с T = Foo] ' –

+0

составление с gcc 4.4.2 –

2

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

error: initializing argument 1 of ‘static char (& is_complete::pass(T))[2] [with 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 работать в первую очередь.

2

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

Это написано на 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 > {}; 

Online demo.

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

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

Default arguments are evaluated each time the function is called.

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

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

-1

Мои 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 
    { 
    }; 
+0

Для правильной работы я должен был внести некоторые изменения: 'namespace detail { template struct is_incomplete: :: std :: true_type {}; template struct is_incomplete : :: std :: false_type {}; struct is_incomplete: detail :: is_incomplete :: type {}; ' – ScootyPuff

+0

@ScootyPuff Сделайте редактирование, scotty, я не буду возражать. – user1095108

+0

Это скажет вам, является ли класс неполным при первом запросе, но он плохо сформирован (без диагностики и не будет работать на практике), если вы снова используете его после определения класса. – Potatoswatter

5

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

Итак, вот вы где.

С VS2015 (V140), г ++> = 4.8.1, лязг> = 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 
{}; 

Благодаря Бат-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 assert that template typename T is NOT complete?

Смежные вопросы