为什么即使参数标记为“const”,也会调用复制构造函数?

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

提问人:Eduard Nicodei 提问时间:7/28/2019 更新时间:7/28/2019 访问量:142

问:

请考虑以下两个函数:

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

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

通话时

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

编译器似乎仍然将一个临时对象(通过复制构造函数创建)传递给 ( https://godbolt.org/z/p2ID1dfoo )

它为什么要这样做?x 是 const,因此它不会被 改变,因此编译器仍然应该能够以与调用相同的方式直接传递。fooafoo2(a)

旁注:我正在查看 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&)

我猜答案与 的定义中被删除的事实有关,但我不知道为什么。对不起,如果我错过了一些明显的东西。constfoo

谢谢!

C++ 参数传递 常量 传递引用 复制构造函数

评论

3赞 Borgleader 7/28/2019
不,它不能,你按值传递它,它必须制作一个副本
1赞 Boris Lipschitz 7/28/2019
const 或没有 const,您仍然按值传递,因此编译器在这里不进行此优化并没有错。如果你疯狂到通过(const)值,然后抛弃const怎么办?Anywa,为什么要费心这样做并指望编译器为您优化它?为什么不直接通过 ref,就像你在第二个例子中所做的那样?

答:

0赞 Sid S 7/28/2019 #1

按值传递时,编译器必须创建一个副本。

按值传递的效果只是不允许函数修改它接收的副本。这样做比路过没有好处 - 相反,它的效率较低,因为它涉及副本。constconst &

1赞 Thomas Sablik 7/28/2019 #2

编译器不优化它是对的。你可以做这样的事情

#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;
}

这甚至可能发生在后台,编译器不会知道。

3赞 Nikos C. 7/28/2019 #3

对于非引用参数,当编译器存储函数的签名时,实际上会忽略它。例如,给定以下函数原型:const

void func(std::string);

func可以使用此签名实现:

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

因此,对如何传递值参数没有任何影响。它们被复制。仅对参数在函数实现中的使用方式有影响。constconst