I've seen code with std::string_view with the following signatures:
void foo(std::string_view const &); // 1
void foo(std::string_view const);   // 2
Which is more correct? Which is more efficient? (I assume the answer to both is one in the same)
I've seen code with std::string_view with the following signatures:
void foo(std::string_view const &); // 1
void foo(std::string_view const);   // 2
Which is more correct? Which is more efficient? (I assume the answer to both is one in the same)
 
    
    Theoretically, as string_view is non-owning, it can already be considered a reference. So using by using a reference to a string_view, you get a reference to a reference.
But it actually seems to depend on the level of optimization you set the compiler to. Consider the following code:
#include <iostream>
#include <string_view>
void foo(std::string_view str) {
    std::cout << str;
}
void bar(std::string_view const &str){
    std::cout << str;
}
int main() {
    foo("test1");
    bar("test2");
}
foo should be more efficient than bar, right?
If you compile with gcc 10.1 without optimizations, you get the following assembly for foo and bar:
foo(std::basic_string_view<char, std::char_traits<char> >):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     rax, rdi
        mov     rcx, rsi
        mov     rdx, rcx
        mov     QWORD PTR [rbp-16], rax
        mov     QWORD PTR [rbp-8], rdx
        mov     rdx, QWORD PTR [rbp-16]
        mov     rax, QWORD PTR [rbp-8]
        mov     rsi, rdx
        mov     rdx, rax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string_view<char, std::char_traits<char> >)
        nop
        leave
        ret
bar(std::basic_string_view<char, std::char_traits<char> > const&):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     QWORD PTR [rbp-8], rdi
        mov     rax, QWORD PTR [rbp-8]
        mov     rdx, QWORD PTR [rax]
        mov     rax, QWORD PTR [rax+8]
        mov     rsi, rdx
        mov     rdx, rax
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string_view<char, std::char_traits<char> >)
        nop
        leave
        ret
You can see that foo has more operations than bar. Seems less efficient.
However, if you set the compiler optimization to -O1, you get the following assembly
foo(std::basic_string_view<char, std::char_traits<char> >):
        sub     rsp, 8
        mov     rdx, rdi
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        add     rsp, 8
        ret
bar(std::basic_string_view<char, std::char_traits<char> > const&):
        sub     rsp, 8
        mov     rsi, QWORD PTR [rdi+8]
        mov     rdx, QWORD PTR [rdi]
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
        add     rsp, 8
        ret
Now foo requires one less operation.
 
    
    std::string_view acts as a pointer to a std::string or a char* C string. It contains a pointer and a length. There is no need to pass it by reference. Always use a value and copy it. 
Never store it anywhere, or if you do remember it is a pointer, not the actual thing.
