Abstract: This discusses safespan and nspan. Motive: It's easy to forget to check the size of an array and containers. Ex 1: if (argv[i] == '-o') outfile = argc[i+1]; //oops, out of bounds Ex 2: while(left 0 2. Subscript operator may only accept A. Literal known to be within bounds B. A (loop) variable that has been checked against the length 3. Invalidates rule 2 when modified Let us rewrite the first example Ex 3: int main(int argc, char *argv[]) { std::string outfile; std::safespan args{argv, argc}; while(args.size()) { if (args[0][0] == '-' && args.size() > 1) { if (args[0][1] == 'o') { //char* isn't safespan so bounds are not checked outfile = args[1]; //ok, checked above args=args.slice(2) //ok, 2 may put this to the end which means args is empty continue; - args is initialized with a valid pointer and length - Because args.size() only enters the loop when size > 0 we may access args[0] - args.size() > 1 in the if, it unlocks args[1] inside the second if - 'args=args.slice(2)' modifies the object, so subscript is illegal until size/bounds is checked again Ex 4: for (int i=0; i data); void test() { char valid_png_header[] = ... assert(IsPNGHeader(valid_png_header)); } int test_file(std::string filename) { std::safespan file = LoadFile(filename); if (file.size() < 1234) return assert(IsPNGHeader(file)) - IsPNGHeader wants a pointer to a char type with at least 8 elements - valid_png_header is a constant known to be 8 bytes so the compiler allows an implicit conversion - No bounds check happens in test() or IsPNGHeader - LoadFile may return a 0 length pointer so nspan can not be used (length isn't known at compile time) - Since file is a safespan type, the compiler pays attention that the if statement compares to a literal and returns. After the return statement, the compiler can reason that file.size() >= 1234 - Since 1234 is > 8 the compiler may cast the data pointer as a nspan, where the rest of the code doesn't do any runtime length checks - test_file does exactly one size check It's done with safespan which contains both pointer and length. There's no way a compiler can know the length of a file created and accessed in the future at runtime. Ex 6: check8(const std::nspan data) { /* empty*/ } check36(const std::nspan data) { check8(data); check8(data.slice(20)); //creates nspan } check4096(const std::nspan data) { check36(data) } int main() { char buf[4096]={0}; check4096(buf) } Here the program requires 0 runtime bounds checking. Because it is useful to have implicit conversions it should be ambiguous to use function overloading when the nspan parameter is the only difference