When you call the vector version, the initializer list is used to create a temporary std::vector , which is then passed to the function via the const reference. This is possible because std::vector has a constructor that takes std::initializer_list<T> as an argument.
However, gsl::span does not have such a constructor, and {1,2,3} does not have a type, nor can it be accepted by the template constructor you mentioned (except that std::initializer_list<T> not satisfied with the container concept anyway).
One (ugly) workaround would, of course, be to explicitly create a temporary array:
func(std::array<int,3>{ 0,1,2,3 });
I donβt see any special reason why gsl::span should not have a constructor that accepts std::initializer_list , but keep in mind that this library is still quite new and is under active development. So, perhaps, this is what they lose sight of, did not have time to implement, were not sure how to act correctly or there are some details that would make this design dangerous. It is probably best to ask the developers directly on github.
EDIT:
As @Nicol Bolas explains in his comment, this was by design because a list of initializers, such as {0,1,2,3} (and elements inside), is a temporary object and since gsl::span not its own container ( he does not accept ownership of the elements), they think it would be too easy to accidentally create a gsl::span that contains a dangling link to these temporary elements.
So for now, everything will be fine:
func({ 0,1,2,3 });
because the lifetime of the initializer list ends after the function finishes, something like this would create a dangling link:
gsl::span<const int> data{ 0,1,2,3 }; func(data);