2

As an example, say I have the class

class A{
    B& foo;
};

and I want to initialize this class with a constructor that takes in a vector and an index (just for example).

So I get

    explicit A(std::vector<B>& lst, int index):
        foo(lst[index])
    {};

However, accessing a random index in the vector is unsafe, since it would be UB if it was out of bounds. So, I throw an exception

    explicit A(std::vector<B>& lst, int index):
        foo(lst[index])
    {
        if (index >= lst.size()){
            //throw blah blah
        }
    };

However, this doesn't actually save me at all, because the member initializer list is ran before the constructor body. So the possible out of bounds access would happen first, and only after that the check runs.

My current thoughts to solve this are:

  1. Assign values in the constructor body.

    • everything is default constructed first, repeats work
    • I know it's not good practice to store references as class members, but this also means that in general we can't throw on any non-default constructable values class members
      • though I guess it would technically make sense to never be able to throw on a non-default constructable object for exception safety
  2. Make this check happen before calling the constructor

    • Hard to enforce

Are there any language abilities that let me throw an exception early? And if not, what is standard practice for this situation.

k huang
  • 409
  • 3
  • 10
  • 9
    use `std::vector::at`: `foo(lst[index])` -> `foo(lst.at(index))` – NathanOliver Aug 08 '22 at 17:53
  • 4
    ^ is the right way for this case, but in the general case, use a helper function: `foo(helper(lst, index))` where `helper` looks like `B& helper (std::vector& lst, int index)` and does all of the required heavy lifting, testing, and exception-throwing. – user4581301 Aug 08 '22 at 18:35
  • @user4581301 Well, even that is insufficiently general. The truly general case may also require an auxiliary constructor. – HTNW Aug 08 '22 at 22:31
  • 4
    I think y'all should stop posting answers as comments. – Brian Bi Aug 09 '22 at 00:11
  • I think you should just not do this. Why not just have `A(B & b) : foo(b) { }` and let the caller deal with looking up the index in the vector? – Goswin von Brederlow Aug 09 '22 at 15:47
  • @GoswinvonBrederlow this is just a minimal example, there is more stuff going on with my case that I removed – k huang Aug 09 '22 at 19:32

1 Answers1

2

Besides the solutions in the comments, you can use the ternary operator:

explicit A(std::vector<B>& lst, int index):
    foo(lst[index < lst.size() ? index : throw blah_blah()])
{}

It requires no helper functions and allows customizing your throw.

passing_through
  • 1,778
  • 12
  • 24