Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is copy constructor called even when parameter is marked 'const'?

Consider the following two functions:

int foo(const std::string x) {
    return x.length();
}

int foo2(const std::string& x2) {
    return x2.length();
}

When calling

std::string a("hello world!");
foo(a);

The compiler seems to still pass in a temporary object (created via copy constructor) to foo ( https://godbolt.org/z/p2ID1d )

Why does it do that? x is const, therefore it will not be changed by foo, therefore the compiler should still be able to pass directly a in the same way it does then calling foo2(a).

Side note: I'm looking at the function types generated by godbolt:

foo(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >)

foo2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)

I'm guessing the answer has something to do with the fact that the const gets dropped in the definition of foo, but I have no idea why. Sorry if I'm missing something obvious.

Thanks!

like image 456
Eduard Nicodei Avatar asked Dec 19 '25 03:12

Eduard Nicodei


2 Answers

For non-reference parameters, const is actually ignored when the compiler stores the function's signature. For example, given this function prototype:

void func(std::string);

func can be implemented with this signature:

void func(const std::string) // This is NOT an overload.
{}

So const has no effect whatsoever on how value parameters are passed. They are copied. The const only has an effect on how the parameter is used within the function's implementation.

like image 111
Nikos C. Avatar answered Dec 21 '25 18:12

Nikos C.


The compiler is right to not optimize it. You could do something like

#include <iostream>
#include <thread>


void foo(const std::string x) {
    using namespace std::chrono_literals;
        std::this_thread::sleep_for(1s);
    std::cout << x.length() << '\n';
}

int foo2(const std::string& x2) {
    using namespace std::chrono_literals;
        std::this_thread::sleep_for(1s);
    std::cout << x2.length() << '\n';
}

int main() {
    std::string a("hello world!");
    std::thread t1([&]{
        foo(a);
    });
    std::thread t2([&]{
        foo2(a);
    });
    a = "";
    t1.join();
    t2.join();
    return 0;
}

This can even happen in the background and the compiler won't know.

like image 32
Thomas Sablik Avatar answered Dec 21 '25 17:12

Thomas Sablik