Is there any difference between get_the_title() and single_post_title()?

single_post_title() and get_the_title() work completely differently.

get_the_title() (or the_title(), which works the same) will get the title for the current post in the loop:

<?php 
if ( have_posts() ) : 
    while ( have_posts() ) : the_post(); 
        the_title();
    endwhile; 
endif; 
?>

So it’s the function to use to get the post title for each post in the template for the blog and archives, when used inside the loop. get_the_title() can also be used to get the title for a specific post. This can be done by passing the ID to the post whose title you want:

echo get_the_title( 123 );

single_post_title(), on the other hand, gets the title of the queried object. When you are on a single post or page, the “queried object” will be a WP_Post representing that post or page, and single_post_title() gets the title of that post. I found this article which describes what the queried object is in more detail.

So on a single template this will almost certainly be the same as the current post in the loop. However, because this function always returns the title of the queried object, you can use it to get the title of the current page outside of the loop, or inside a secondary loop.

Also note, that because the queried object of the blog and archives isn’t a single post or page, single_post_title() will not work on those pages. For those pages you want to use the_archive_title(), or for taxonomy archives, single_term_title().