Search code examples
c++constructorpass-by-referenceimplicit-conversiontemporary-objects

Implicitly convertible argument, but of ref type


#include <iostream>
#include <string>

void foo(const std::string & s)
{
    std::cout << s;
}

const char * text = "something";


int main()
{
    foo( text );
}

I started wondering what is happening here because gdb reports s == "". What is supposed to happen according to a recent (say C++17) version of the standard?

Via https://godbolt.org/ I can see an std::string c-tor, so perhaps one is constructed and passed by reference, then destroyed upon function termination.


Solution

  • The class template std::basic_string has implicit conversion constructor

    basic_string(const charT* s, const Allocator& a = Allocator());
    

    So in this call

    foo( text );
    

    a temporary object of the type std::string is created and a reference to it is used as an initializer of the function parameter. At the end of this statement the temporary object is destroyed.

    Here is a demonstrative program that shows what is happening under the hood using a sample class.

    #include <iostream>
    
    class String
    {
    private:
        const char *s;
    
    public:
        String( const char *s ) :s( s ) 
        {
            std::cout << "String( const char * ) is called\n";
        }
    
        ~String()
        {
            std::cout << "~String() is called\n";
        }
    
        friend std::ostream & operator <<( std::ostream &os, const String & s )
        {
            return os << s.s;
        }
    };
    
    void foo( const String & s )
    {
        std::cout << s << '\n';
    }
    
    const char * text = "something";
    
    
    int main()
    {
        foo( text );
    } 
    

    The program output is

    String( const char * ) is called
    something
    ~String() is called
    

    The program will not work if the constructor would be declared with the function specifier explicit.

        explicit String( const char *s ) :s( s ) 
        {
            std::cout << "String( const char * ) is called\n";
        }
    

    To make the program to work you will need to call the constructor explicitly. For example

    foo( String( text ) );