28

回答完这个问题后,我试图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 规则,一般无法解决这个问题,但是有一个特定于平台的解决方案对我有用。

4

9 回答 9

17

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

于 2009-12-24T01:14:41.857 回答
14

可能有点晚了,但到目前为止,还没有 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 不完整?

于 2016-05-12T16:54:53.497 回答
9
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;
};
于 2009-10-26T15:03:16.113 回答
5

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

错误:初始化 'static char (& is_complete::pass(T))[2] [with T = Foo]'的参数 1</p>

在我的 Mac 上,使用 G++ 4.0.1 评估不完整的is_complete<Foo>::value地方比编译器错误更糟糕。struct Foo;true

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

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

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

于 2009-12-23T15:44:24.687 回答
3

解决这个问题需要在特征模板的默认参数中执行计算,因为尝试更改模板的定义违反了 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 > {};

在线演示。

默认参数在模板命名的地方进行评估,因此它可以根据上下文在不同的定义之间切换。每次使用都不需要不同的专业化和定义;你只需要一个 fortrue和一个 for false

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

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

但请注意,在模板中使用它几乎肯定会违反 ODR。在不完整类型上实例化的模板不能与在完整类型上实例化的模板做任何不同的事情。我个人只想要这个static_assert

顺便说一句,如果您想另辟蹊径并实现__COUNTER__使用模板和重载的功能,这个原则也可能会有所帮助。

于 2013-07-16T09:12:29.617 回答
2

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

于 2009-10-26T14:35:30.457 回答
2

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

答案就在这里。我没有将它粘贴在下面,以免错误地获得它的功劳。

于 2018-04-05T15:25:06.870 回答
0

这是一个老问题,但建议的答案不适用于某些类型,例如函数引用类型或 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> {};

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

于 2021-07-12T15:59:05.857 回答
0

在 C++17 中,可以使用std::void_t带有sizeof操作符的可变参数。

此外,使用sizeof运算符还可以检查不完全专业化的类模板是否“是”(即在实例化时)是否完整。

您只需要定义一个虚拟标签,它是一个完整的类型。

struct any_t
{
};

template<template<typename> class, typename = void>
struct is_complete : std::false_type
{
};

template<template<typename> class GenericClass>
struct is_complete<GenericClass, std::void_t<decltype(sizeof(GenericClass<any_t>))>> :
  std::true_type
{
};

template<typename T>
struct complete
{
    T t;
    void speak() { return t.speak(); }
};

template<typename>
struct incomplete;

static_assert(is_complete<complete>());
static_assert(!is_complete<incomplete>());

仅当您尝试实例化对象时,对tin 的任何调用都会导致编译错误。complete

using t = complete<any_t>; // ok
auto c = complete<any_t>(); // error. any_t has no member `speak()`
于 2021-08-03T18:47:26.813 回答