c++实现我自己的static_assert

C++ implement my own static_assert

本文关键字:assert static 自己的 实现 我自己 c++      更新时间:2023-10-16

作为一个学习项目,我正在编写我自己的模板元编程static_assert。我在网上找到了一个元编程技巧:尝试创建一个大小为0的数组,这将无法编译。所以我使用了两种几乎相同的方法:在Visual Studio上,一种可以工作,另一种不行,但我不明白有什么区别。在c++ 5.4.0中,两者都不能工作(即使使用"-std=c++14"标志)。为什么不呢?

//This correctly aborts the compile on Visual Studio 2015. 
//But on g++ it doesn't work (not even with the "-std=c++14" flag) .
template <bool b>
inline void my_static_assert_function()
{
    char member[b]; //if b is false, this is 0-length and fails to compile. 
}
//On Visual Studio 2015, this does give a warning, but does not
//abort the compile. Why not? It seems virtually identical to the
//previous one. And on g++, it doesn't even warn. 
template <bool b>
struct my_static_assert_struct
{
    char member[b]; //if b is false, this *warns* but compiles.
};
int main()
{
    my_static_assert_function<1 == 2>(); //This aborts the compile, great.
    my_static_assert_struct<1 == 2> c; //This does NOT abort the compile???
}

问题#1——为什么"g++ -std=c++14 main.cpp"允许在没有警告的情况下编译它?my_static_assert_function不应该在那里工作吗?我用的是5.4.0版本。

问题#2——在Visual Studio 2015上,my_static_assert_function编译失败,但my_static_assert_struct编译时只给出一个警告。但是有什么区别呢?如果另一个不起作用,一个怎么能起作用呢?

正如@Kerrek SB在评论中提到的,gcc使用一些非标准的ISO c++扩展来允许零大小的数组,尽管它会给你一个警告。一个更优雅的替代方案是通过std::enable_iffalse进行SFINAE,就像这样

#include <iostream>
#include <type_traits>
template<bool b, typename std::enable_if<b>::type* = nullptr>
void my_static_assert() 
{
    std::cout << "Assertion OKn";
}
int main()
{
    my_static_assert < (1 < 2) > (); // ok
    //my_static_assert < (1 > 2) > (); // fails to compile
}

Live on Coliru

另一种选择(我认为是由Andrei Alexandrescu首先提出的)是保留未定义的泛型模板,并且只定义true专门化。然后,如果尝试实例化false专门化,就会得到编译时错误,因为无法实例化不完整类型。在下面的例子:

template<bool> // generic
struct my_static_assert;
template<>
struct my_static_assert<true>{};
int main()
{
    my_static_assert < (1 < 2) >{}; // ok
    my_static_assert < (1 > 2) >{}; // fails to compile
}

Live on Coliru

好吧,这没有使用模板,但我记得在某处看到过这个:

#define __static_assert(con, id) static int assert_ ## id [2 * !!(con) - 1];
#define _static_assert(con, id) __static_assert(con, id) //expanding any macro that could be in `id` argument
#define static_assert(con) _static_assert(con, __COUNTER__)

解释:

  • !!(con)使任何非零数字为真(1)为零变为为假(0)
  • 通过减去一个得到-1当条件不满足数组不能有负大小 ->编译时错误
  • 我们不希望有多个具有相同名称的数组,为此我们使用__COUNTER__宏来组合名称,但为此我们需要首先展开它