static_assert on initializer_list::size()

前端 未结 5 1297
执笔经年
执笔经年 2020-12-05 00:29

Why is std::initializer_list<_E>::size not allowable in a static_assert, even though it\'s declared as a constexpr in my libstdc++ (v. 4.6)?

For example, the foll

相关标签:
5条回答
  • 2020-12-05 00:47

    I haven't really figured out what's going on here.

    If I say

    const std::initializer_list<double> li = { 1, 2.5, 3.7, 4.3 };
    static_assert(li.size() == 4, "fail");
    

    I get a complain that 'li' was not declared 'constexper'.

    But if I say

    constexpr std::size_t dSize(std::initializer_list<double> di)
    {
        return di.size();
    }
    

    then

    static_assert(dSize({1, 2.5, 3.7, 4.3}) == 4, "failed");   // works
    

    but

    static_assert(dSize(li) == 4, "failed");
    

    fails with "error: the value of 'li' is not usable in a constant expression"

    This is all with -std=c++11

    So, somehow, an initializer list passed into an arg list can be part of a constant expression, but an initializer list just declared as a const variable can't.

    0 讨论(0)
  • 2020-12-05 00:49

    Use following syntax:

    LIVE DEMO

    #include <initializer_list>
    
    template<class T, int Length>
    class Point
    {
        std::initializer_list<T> data;
    public:
        constexpr Point(std::initializer_list<T> init)
            : data
            (
                init.size() == Length ?
                init : throw 0
            )
        {}
    };
    
    int main()
    {
        constexpr Point<int, 3> a{{1,2,3}};
        constexpr Point<int, 2> b{{1,2,3}}; // compile time error
    }
    

    Refer following SO.


    EDIT: Interesting that works on GCC 4.8.1, but does not work on Clang 3.4. Maybe this is related to constexpr of .size() (afaik, in C++14 it is constexpr).

    0 讨论(0)
  • 2020-12-05 00:55

    From my discussion with @Evgeny, I realized that this just works (with gcc 4.8 c++11) and may as well do the size check by only accepting a compatible size in the initializer list (in main).

    (code link: http://coliru.stacked-crooked.com/a/746e0ae99c518cd6)

    #include<array>
    template<class T, int Length>
    class Point
    {
      public:
        Point(std::array<T, Length> init)
        {
    //not needed//      static_assert(init.size() == Length, "Wrong number of dimensions");
        }
    };
    
    int main()
    {
      Point<int, 3> q({1,2,3}); //ok
    //  Point<int, 3> q2({1,2,3,4}); //compile error (good!)
      Point<int, 3> q2({1,2}); // ok, compiles, same as {1,2,0}, feature?
      return 0;
    }
    
    0 讨论(0)
  • 2020-12-05 00:59

    "Initializer lists" are just horrible kludges.

    Don't:

    #include <initializer_list>
    
    template<typename T>
    void Dont(std::initializer_list<T> list) { // Bad!
        static_assert(list.size() == 3, "Exactly three elements are required.");
    }
    
    void Test() { Dont({1,2,3}); }
    

    Do:

    template<typename T, std::size_t N>
    void Do(const T(&list)[N]) { // Good!
        static_assert(N == 3, "Exactly three elements are required.");
    }
    
    void Test() { Do({1,2,3}); }
    
    0 讨论(0)
  • 2020-12-05 01:00

    The compiler says that init is the problem, not init.size().

    I guess that the constructor could be called from different places with different length initializers.

    (To elaborate: You're trying to write a static_assert that depends on the run-time value of the variable init, namely how many elements it has. static_asserts have to be evaluable at the time the function is compiled. Your code is analogous to this trivially invalid example:)

    void foo(int i) { static_assert(i == 42, ""); }
    int main() { foo(42); }  // but what if there's a caller in another translation unit?
    
    0 讨论(0)
提交回复
热议问题