How to write `is_complete` template?

前端 未结 7 610
耶瑟儿~
耶瑟儿~ 2020-11-29 07:35

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

相关标签:
7条回答
  • 2020-11-29 07:50

    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?

    0 讨论(0)
  • 2020-11-29 07:53

    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.

    0 讨论(0)
  • 2020-11-29 07:54

    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.

    0 讨论(0)
  • 2020-11-29 07:56
    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;
    };
    
    0 讨论(0)
  • 2020-11-29 07:57

    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.

    0 讨论(0)
  • 2020-11-29 08:03

    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.

    0 讨论(0)
提交回复
热议问题