如何写`is_complete`模板?
-
06-07-2019 - |
题
在回答了这个问题后,我试图在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吗?
解决方案
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 预定义的宏不是标准的一部分,所以它不适用于每个编译器。
其他提示
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;
};
可能有点晚了,但到目前为止,没有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 >
{};
这个灵感来自互联网和静态断言模板类型名称T是不完整的?
我担心你无法实现这样的is_complete
类型特征。 @Alexey给出的实现无法在G ++ 4.4.2和G ++ 4.5.0上编译:
错误:初始化<!>#8216的参数1;静态字符(<!> amp; is_complete :: pass(T))[2] [与T = Foo] <!>#8217;
在我的Mac上,使用G ++ 4.0.1评估is_complete<Foo>::value
struct Foo;
true
不完整的收益率T
,这比编译器错误更糟糕。
is_complete<T>
在同一程序中可以是完整的也可以是不完整的,具体取决于翻译单元,但它总是相同的类型。因此,如上所述,__COUNTER__
总是同一类型。
因此,如果您尊重 ODR ,则无法is_complete<T, int>
评估不同值取决于它的使用位置;否则就意味着你对IS_COMPLETE
哪个ODR禁止有不同的定义。
解决这个问题需要在特征模板的默认参数中执行计算,因为尝试更改模板的定义会违反ODR规则(尽管__COUNTER__
和namespace {}
的组合可以解决ODR问题)。 / p>
这是用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
。
规则在<!>#167; 8.3.6 / 9中给出,它同样适用于函数默认参数和默认模板参数:
每次调用函数时都会计算默认参数。
但要注意,在模板中使用此功能几乎肯定会违反ODR。在不完整类型上实例化的模板与在完整类型上实例化的模板不能做任何不同的操作。我个人只想要这个static_assert
。
顺便提一下,如果你想采取另一种方式,这个原则也可能会有所帮助实施 <=>使用模板和重载的功能。
只是为了表示对一个不相关的问题的答案(我没有给出)给出is_complete<T>
模板的解决方案。
答案是此处。我不会在下面粘贴它,以免错误地获得它。
我在标准中找不到任何保证不完整类型的sizeof将产生0的内容。但是,如果T在某个时刻不完整,但在该翻译单元中稍后完成,那么所有引用都需要to T指的是相同的类型 - 所以当我读到它时,即使在调用模板时T不完整,如果T在该翻译单元的某处完成,也需要说它已经完成。