0

If I have a simple structure like

struct Point { int x, y; };

then I can do

int main()
{
    Point p1 = { 10 };          // x = 10, y = 0
    Point p2 = { 10, 20 };      // x = 10, y = 20
    Point p3 = { 10, 20, 30 };  // Compile error: too many initializers for ‘Point’
    return 0;
}

I now want to have the same behaviour when initializing Point with Point becoming a class but with x and y becoming private and using accessors, etc.

My first attempt was

class Point
{
public:
    Point( std::initializer_list<int> init )
    {
        switch( init.size() )
        {
            case 0: x = 0; y = 0; break;
            case 1:
            {
                auto iter = init.begin();
                x = *iter++; y = 0;
                break;
            }
            case 2:
            {
                auto iter = init.begin();
                x = *iter++; y = *iter++;
                break;
            }
            default:
                throw 0;
                break;
        }
    }
private:
    int x, y;

};

which kinda works but changes the compile time error into a runtime error. The question now is: How do I cause this class to behave the same as the simple struct, i.e. cause a compile time error when the initializer list is too large?

Looking around I found

Reading through the answers and comments I understand some of the constexpr and static_assert issues but am still no nearer to finding a solution to my question. Is it possible to cause a compile time error in C++11 (or C++14)? The compiler definitely knows all it needs to and it seems a likely enough thing for someone to want to do that the standard would have rolled it in somehow.

Community
  • 1
  • 1
user2746401
  • 3,157
  • 2
  • 21
  • 46
  • Why do you use std::initializer_list for initialization of not-container member? Is it necessary in your case to initialize the struct from a list/struct literal? – Valentin H Jan 16 '15 at 11:26
  • The above code is a simple toy example to try to illustrate the question "How to error at compile based on the size of an initializer_list?". The actual use is/will be in a container class backed by small (compile time known based on the target hardware platform) amount of memory. These blocks of memory are then filled using brace initializers at compile time. The code is transitioning from a C-like interface to C++ style. Sadly, I can't share the actual code :( – user2746401 Jan 16 '15 at 14:47

2 Answers2

3

By replacing the initializer list constructor with one like the following...

Point(int x, int y) :m_x(x), m_y(y) {}

Note, I rewrote private variables x and y as m_x and m_y.

Now when you attempt to initialize a Point object with more than 2 arguments, you will get a compiler error similar to the one you had when Point was a struct.

dspfnder
  • 1,135
  • 1
  • 8
  • 13
  • 1
    Thank you but this will break the initializer list syntax used to initialize the objects. I want to write `Point p = { 10, 20 }` rather than `Point p( 10, 20 )` as your solution would require me to do. – user2746401 Jan 16 '15 at 11:25
  • 3
    why would you want that? – Axalo Jan 16 '15 at 11:26
  • 4
    @user2746401 you can write `Point p = { 10, 20 }` with this constructor as well. – Anton Savin Jan 16 '15 at 12:15
  • @Axalo There's a lot of code that already uses the brace initializer syntax and I don't want to break it while moving from a struct to a class. – user2746401 Jan 16 '15 at 14:30
  • @AntonSavin Good point. I was worried about the single argument case, i.e. `Point p = { 10 }`, but that's fine as I can use a default value to the `y` argument to the constructor. – user2746401 Jan 16 '15 at 14:40
  • I agree with Axalo in asking - why would you want that kind of syntax in the first place? It makes Point look like an array. If other people look through your code they may be misled to start with. – Lloyd Crawley Jan 16 '15 at 16:49
  • @LloydCrawley @Axalo The brace initialization was an addition in c++11 to uniform the syntax and a Point is a perfect candidate to syntax improvment, with it you can do something like that `Distance( {1,2}, {4,5} )` instead of `Distance( Point(1,2), Point(2,4) )`. Which one do you prefer ? A mislead programmer would be a programmer that never looked at the c++11 changes… – galop1n Jan 17 '15 at 01:14
  • @galop1n That's interesting, but truthfully I prefer the second one. More information (within reason) is always better. – Lloyd Crawley Jan 17 '15 at 10:56
-1

There is no compile time way to retrieve a std::initializer_list size, but you do not need it.

The initializer like syntax is named uniform initialization, part of the c++11 to unify, as the name self defined, the syntax.

Here a full example that work for your Point, as you can see, a constructor with arguments can be a match for a initializer list.

#include <iostream>

class Point {
public:
    Point() = default;

    Point( int x, int y ) : x_{x}, y_{y} {}
    Point( int x ) : x_(x) {}

    // or in your case, you can use a default argument
    //Point( int x, int y = int{} ) : x_{x}, y_{y} {}

    int X() const { return x_; }
    int Y() const { return y_; }
private:
    int x_{};
    int y_{};
};

void DisplayPoint( Point const & p) {
    std::cout << "( " << p.X() << ", " << p.Y() << " )" << std::endl;
}

Point GetAPoint() {
    return { 3, 5 };
}

int main() {
    DisplayPoint( {} );
    DisplayPoint( { 1 } );
    DisplayPoint( { 1, 2 } );
    DisplayPoint( GetAPoint() );
    DisplayPoint( Point( 5, 3 ) ); // pre c++11
    DisplayPoint( Point{ 5, 3 } ); // in the case the constructor is explicit
}
galop1n
  • 8,573
  • 22
  • 36