如何编写“is_complete”模板?

发布于 2024-08-09 03:17:00 字数 830 浏览 7 评论 0原文

回答这个问题后,我试图找到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 规则,一般来说这个问题是无法解决的,但是确实有适合我的平台特定解决方案

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

吃颗糖壮壮胆 2024-08-16 03:17:00

Alexey 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__ 预定义宏不是标准的一部分,因此它不适用于每个编译器。

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.

岁吢 2024-08-16 03:17:00

可能有点晚了,但到目前为止,还没有 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
{};

感谢 Bat-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 >
{};

这个灵感来自互联网和 静态断言模板类型名 T 不完整?

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?

大姐,你呐 2024-08-16 03:17:00
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;
};
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;
};
拔了角的鹿 2024-08-16 03:17:00

恐怕您无法实现这样的 is_complete 类型特征。 @Alexey 给出的实现无法在 G++ 4.4.2 和 G++ 4.5.0 上编译:

错误:初始化“static char (& is_complete::pass(T))[2] [with T = Foo]”的参数 1

,使用 G++ 4.0.1 评估 is_complete::value 其中 struct Foo; 不完整会产生 true,这比编译器错误更糟糕。

T 在同一程序中可以是完整的,也可以是不完整的,具体取决于翻译单元,但它始终是相同的类型。因此,如上所述,is_complete 也始终是相同的类型。

因此,如果您尊重 ODR,则不可能有 is_complete 根据使用位置评估不同的值;否则,这意味着您对 ODR 禁止的 is_complete 有不同的定义。

编辑:作为公认的答案,我自己破解了一个解决方案,该解决方案使用 __COUNTER__ 宏在每次 IS_COMPLETE< 时实例化不同的 is_complete 类型使用 /code> 宏。然而,对于 gcc,我一开始就无法让 SFINAE 工作。

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.

梦在深巷 2024-08-16 03:17:00

解决此问题需要在特征模板的默认参数中执行计算,因为尝试更改模板的定义违反了 ODR 规则(尽管 __COUNTER__namespace {} 可以解决 ODR)。

这是用 C++11 编写的,但可以调整为在最近的 C++11 兼容编译器的 C++03 模式下工作。

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

该规则在 §8.3.6/9 中给出,它同样适用于函数默认参数和默认模板参数:

每次调用函数时都会评估默认参数。

但要注意,在模板中使用它几乎肯定会违反 ODR。在不完整类型上实例化的模板不得执行与在完整类型上实例化时不同的任何操作。我个人只希望将其用于static_assert

顺便说一句,如果您想走另一条路,并且 使用模板和重载实现__COUNTER__的功能。

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.

几味少女 2024-08-16 03:17:00

只是插话以表明对不相关问题的答案(不是我给出的)为 is_complete 模板提供了解决方案。

答案就在这里。我不会将其粘贴在下面,以免错误地获得荣誉。

Just chiming in to signal that an answer (not given by me) to an unrelated question gives a solution for the is_complete<T> template.

The answer is here. I'm not pasting it below in order to not mistakenly get credit for it.

方觉久 2024-08-16 03:17:00

我在标准中找不到任何内容可以保证不完整类型上的 sizeof 将产生 0。但是,它确实要求如果 T 在某个时刻不完整,但稍后在该翻译单元中完成,则对 T 的所有引用都引用到相同的类型 - 所以当我读到它时,即使 T 在调用模板的地方不完整,如果 T 在该翻译单元中的某个地方完成,则需要说它是完整的。

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.

再可℃爱ぅ一点好了 2024-08-16 03:17:00

这是一个老问题,但建议的答案对于某些类型(例如函数引用类型或 cv 限定函数类型)无法正常工作。

template<typename T, typename = void>
struct is_complete_object : std::false_type {};

template<typename T>
struct is_complete_object<T, std::enable_if_t<(sizeof(T) > 0)>> : std::true_type {};

template<typename T, bool v = std::is_object<T>::value /* true */>
struct is_complete_impl : is_complete_object<T> {};

template<typename T>
struct is_complete_impl<T, false> : std::integral_constant<bool, !std::is_void<T>::value> {};

template <typename T>
struct is_complete : is_complete_impl<T> {};

template<typename T>
struct is_complete<T[]> : std::false_type {};

template<typename T, size_t N>
struct is_complete<T[N]> : is_complete<T> {};

现在这适用于类似函数的类型。

It's an old question, but the proposed answers doesn't work correctly for some types such as function reference type or cv-qualified function types.

template<typename T, typename = void>
struct is_complete_object : std::false_type {};

template<typename T>
struct is_complete_object<T, std::enable_if_t<(sizeof(T) > 0)>> : std::true_type {};

template<typename T, bool v = std::is_object<T>::value /* true */>
struct is_complete_impl : is_complete_object<T> {};

template<typename T>
struct is_complete_impl<T, false> : std::integral_constant<bool, !std::is_void<T>::value> {};

template <typename T>
struct is_complete : is_complete_impl<T> {};

template<typename T>
struct is_complete<T[]> : std::false_type {};

template<typename T, size_t N>
struct is_complete<T[N]> : is_complete<T> {};

Now this will work for function-like types.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文