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
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?
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.
Solving this requires performing the computation in the default argument of the trait template, as attempting to change the definition of a template violates the ODR rule (although a combination of __COUNTER__
and namespace {}
can work around ODR).
This is written in C++11 but can be adjusted to work in C++03 mode of a moderately recent C++11-compatible compiler.
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.
The default argument is evaluated where the template is named, so it can contextually switch between different definitions. There is no need for a different specialization and definition at each use; you only need one for true
and one for false
.
The rule is given in §8.3.6/9, which applies equally to function default arguments and default template-arguments:
Default arguments are evaluated each time the function is called.
But beware, using this inside a template is almost sure to violate the ODR. A template instantiated on an incomplete type must not do anything differently from if it were instantiated on a complete type. I personally only want this for a static_assert
.
Incidentally, this principle may also be helpful if you want to go the other way and implement the functionality of __COUNTER__
using templates and overloading.
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 can't find anything in the standard that guarantees that sizeof on an incomplete type will yield 0. It does require, however, that if T is incomplete at some point, but completed later in that translation unit, that all references to T refer to the same type -- so as I read it, even if T is incomplete where your template was invoked, it would be required to say it was complete if T is completed somewhere in that translation unit.
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.