【问题标题】:C++ implement my own static_assertC++ 实现我自己的 static_assert
【发布时间】:2016-10-27 00:53:05
【问题描述】:

作为一个学习项目,我正在编写自己的模板元编程 static_assert。我在网上找到了一个元编程技巧:尝试创建一个大小为 0 的数组,这将无法编译。所以我使用了两种几乎相同的方法:在 Visual Studio 上,一种有效,另一种无效,但我不明白有什么区别。在 g++ 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 不应该在那里工作吗?我在 ubuntu 上使用 5.4.0。

问题 #2 - 在 Visual Studio 2015 上,my_static_assert_function 无法编译,但 my_static_assert_struct 编译时仅显示警告。但是有什么区别呢?如果另一个不工作,一个怎么能工作?

【问题讨论】:

  • GCC 是一个狡猾的野兽。你真的需要学习它的所有标志才能把它变成一个诚实的编译器。
  • (这就是真正的程序员制作大小为 -1 的数组的原因。)
  • @Kerrek SB:直到 GCC 也找到了扩展的理由。
  • char member[b ? 1 : -1];
  • static_assert 的主要两个目的是: (1) 在允许的任何地方对所有编译器进行一致的工作。 (2) 有用的错误信息。

标签: c++ c++11 templates static-assert


【解决方案1】:

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

#include <iostream>
#include <type_traits>

template<bool b, typename std::enable_if<b>::type* = nullptr>
void my_static_assert() 
{
    std::cout << "Assertion OK\n";
}

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

【讨论】:

    【解决方案2】:

    嗯,这不使用模板,但我记得在某个地方看到过这个:

    #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) 使任何 非零 数字 true (1) 并且 zero 变为 false (0)
    • 当条件不满足时,减去一个你会得到 -1 并且 数组不能有负大小 -> 编译时错误
    • 我们不希望有多个同名的数组,为此我们使用__COUNTER__ 宏来组成名称,但为此我们需要先扩展它

    【讨论】:

    • 这不是 VLA,因为条件应该是 constexpr。另一方面,零大小数组在标准 C++ 中是非法的。如果条件为真,最好将数组大小设置为1
    • 好吧,我用 gcc(以及 g++)和 clang 进行了尝试,它工作得很好,我有个主意……但是你能为我推荐一个更符合标准的编译器吗?跨度>
    • 您不需要更改编译器,只需添加正确的标志:gcc.godbolt.org/z/seesTcacW
    • 谢谢,我不是真正的标志专家,我也检查了参数确实应该是一个常量表达式...
    猜你喜欢
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多