I think my confusion arose from the fact that the lives_ok test of Test::Exception also accepts these type of test constructs.
Now that that's cleared up, you might be interested why Test::Exception
requires subroutine references: it wouldn't be possible to catch the exception thrown otherwise (well, actually you could hack around that, but it wouldn't be as reliable and it would probably mess up the code flow). Inside the Test::Exception code there's probably something like
eval { $coderef->() } # now do something with $@.
All of the Test::Exception test functions also allow you to leave off the "sub", and instead just use curly braces, but that's really just a convenience made possible by the often misunderstood prototype facility; you're still passing a subroutine reference as the first argument.