如何写`is_complete`模板?

时间:2009-10-26 14:21:54

标签: c++ templates typetraits

在回答this问题之后,我试图在Boost库中找到is_complete模板,我意识到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 rule的情况下解决,但是有一个特定于平台solution对我有用。

9 个答案:

答案 0 :(得分:13)

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

答案 1 :(得分: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;
};

答案 2 :(得分:9)

可能有点晚了,但到目前为止,没有C ++ 11解决方案适用于完整和抽象类型。

所以,你在这里。

使用VS2015(v140),g ++&gt; = 4.8.1,clang&gt; = 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 >
{};

这个灵感来自互联网和static assert that template typename T is NOT complete?

答案 3 :(得分:2)

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

  

错误:初始化'static char(&amp; is_complete :: pass(T))[2] [with T = Foo]'

的参数1

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

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

因此,如果您尊重ODR,则is_complete<T>无法根据其使用位置评估不同的值;否则就意味着你对ODR禁止的is_complete<T>有不同的定义。

编辑:作为公认的答案,我自己已经破解了使用__COUNTER__宏的解决方案,每次使用is_complete<T, int>宏时都会实例化不同的IS_COMPLETE类型。然而,使用gcc,我无法让SFINAE首先工作。

答案 4 :(得分:2)

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

Online demo.

评估模板命名的默认参数,以便它可以在不同的定义之间进行上下文切换。每次使用时都不需要不同的专业化和定义;您只需要一个true和一个false

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

  

每次调用函数时都会计算默认参数。

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

顺便提一下,如果您想使用模板和重载,以另一种方式implement使用__COUNTER__的功能,这个原则也会有所帮助。

答案 5 :(得分:1)

只是为了表示对一个不相关的问题给出答案(我没有给出)给出了is_complete<T>模板的解决方案。

答案是here。我不会在下面粘贴它,以免错误地获得它。

答案 6 :(得分:0)

我在标准中找不到任何保证不完整类型的sizeof将产生0的内容。但是,如果T在某个时刻不完整,但在该翻译单元中稍后完成,那么所有引用都需要to T指的是相同的类型 - 所以当我读到它时,即使在调用模板时T不完整,如果T在该翻译单元的某处完成,也需要说它已经完成。

答案 7 :(得分: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> {};

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

答案 8 :(得分: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>());

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

using t = complete<any_t>; // ok
auto c = complete<any_t>(); // error. any_t has no member `speak()`