Here's a variation on Will's answer. It involves an additional cat process, but offers the comfort of heredoc. In a nutshell it goes like this:
f ()
{
echo ok;
}
cat<<EOS
cat <<EOS | sudo bash
$(declare -f f)
f
EOS
If you want more food for thought, try this:
#!/bin/bash
f ()
{
x="a b";
menu "$x"; y='difficult
thing'; y="difficult thing";
echo "a $y to pass";parse";
}
menu ()
{
[ "$1" == "a b" ] &&
echo "here's the menu";
}
cat<<EOS
cat <<EOS | sudo bash
$(declare -f f)
$(declare -f menu)
f
EOS
The output is:
here's the menu
a difficult thing to pass
Here we've got the menu function corresponding with the one in the question, which is "defined elsewhere in the main script". If the "elsewhere" means its definition has already been read at this stage when the function demanding sudo is being executed, then the situation is analogous. But it might not have been read yet. There may be another function that will yet trigger its definition. In this case declare -f menu has to be replaced with something more sophisticated, or the whole script corrected in a way that the menu function is already declared.