How to write `is_complete` template?
After answering this question I was trying to find is_complete
template in Boost library and I realized that there is no such template in Boost.TypeTraits. Why there is no such template in Boost library? How it should look like?
//! 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 );
The code above is not correct, because it is illegal to apply sizeof
to an incomplete type. What will be a good solution? Is it possible to apply SFINAE in this case somehow?
Well, this problem couldn't be solved in general without violating the ODR rule, but there is there a platform specific solution which works for me.
The answer given by Alexey Malistov can be used on MSVC with a minor modification:
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
Unfortunately, the __COUNTER__
predefined macro is not part of the standard, so it would not work on every compiler.
It might be a bit late, but so far, no C++ 11 solution worked for both complete and abstract types.
So, here you are.
With VS2015 (v140), g++ >= 4.8.1, clang >= 3.4, this is working:
template <class T, class = void>
struct IsComplete : std::false_type
{};
template <class T>
struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type
{};
Thanks to Bat-Ulzii Luvsanbat: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/
With 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 >
{};
This one is inspired from the internets and static assert that template typename T is NOT complete?
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;
};
I'm afraid you can't implement such an is_complete
type traits. The implementation given by @Alexey fails to compile on G++ 4.4.2 and G++ 4.5.0:
error: initializing argument 1 of ‘static char (& is_complete::pass(T))[2] [with T = Foo]’
On my Mac, with G++ 4.0.1 evaluating is_complete<Foo>::value
where struct Foo;
is incomplete yields to true
which is even worse than a compiler error.
T
can be both complete and incomplete in the same program, depending on the translation unit but it's always the same type. As a consequence, as commented above, is_complete<T>
is always the same type as well.
So if you respect ODR it is not possible to have is_complete<T>
evaluating to different values depending on where it is used; otherwise it would mean you have different definitions for is_complete<T>
which ODR forbids.
EDIT: As the accepted answer, I myself hacked around a solution that uses the __COUNTER__
macro to instantiate a different is_complete<T, int>
type everytime the IS_COMPLETE
macro is used. However, with gcc, I couldn't get SFINAE to work in the first place.