pass-by-value 및 std :: move의 장점
나는 지금 C ++를 배우고 있으며 나쁜 습관을 들이지 않으려 고 노력합니다. 내가 이해하는 바에 따르면, clang-tidy에는 많은 "모범 사례"가 포함되어 있으며 가능한 한 최선을 다해 고수하려고 노력하지만 ( 아직 좋은 것으로 간주되는 이유 를 반드시 이해 하지는 못하지만) 확실하지 않습니다. 여기에서 권장되는 사항을 이해하십시오.
튜토리얼에서이 클래스를 사용했습니다.
class Creature
{
private:
std::string m_name;
public:
Creature(const std::string &name)
: m_name{name}
{
}
};
이것은 clang-tidy에서 참조 대신 값을 전달하고을 사용해야한다는 제안으로 이어집니다 std::move
. 내가 할 경우, 나는 제안 할 수 name
및 경고 (이 때마다 복사되지 않습니다 보장하기 위해)에 대한 참조를 std::move
하기 때문에 영향을주지 않습니다 name
A는 const
내가 그것을 제거해야합니다 있도록합니다.
경고가 표시되지 않는 유일한 방법은 const
모두 제거하는 것 입니다.
Creature(std::string name)
: m_name{std::move(name)}
{
}
의 유일한 이점은 const
원래 문자열을 엉망으로 만드는 것을 방지하는 것이므로 논리적으로 보입니다 (값으로 전달했기 때문에 발생하지 않음). 하지만 CPlusPlus.com 에서 읽었습니다 .
-표준 라이브러리에서 이동은 이동 된 개체가 유효하지만 지정되지 않은 상태로 남아 있음을 의미합니다. 즉, 이러한 작업 후에 이동 된 개체의 값은 삭제되거나 새 값이 할당되어야합니다. 그렇지 않으면 액세스하면 지정되지 않은 값이 생성됩니다.
이제 다음 코드를 상상해보십시오.
std::string nameString("Alex");
Creature c(nameString);
nameString
값으로 전달 되기 때문에 생성자 내부 std::move
에서만 무효화 name
되고 원래 문자열을 건드리지 않습니다. 그러나 이것의 장점은 무엇입니까? 내용은 어쨌든 한 번만 복사되는 것처럼 보입니다.을 호출 할 때 참조로 m_name{name}
전달하면 전달하면 값으로 전달되고 이동하면됩니다. 나는 이것이 가치를 전달하고 사용하지 않는 것보다 낫다는 것을 이해합니다 std::move
(두 번 복사되기 때문에).
두 가지 질문이 있습니다.
- 여기서 무슨 일이 일어나고 있는지 올바르게 이해 했습니까?
std::move
참조로 전달하고 호출하는 것의 장점이m_name{name}
있습니까?
- 여기서 무슨 일이 일어나고 있는지 올바르게 이해 했습니까?
예.
std::move
참조로 전달하고 호출하는 것의 장점이m_name{name}
있습니까?
추가 오버로드없이 쉽게 파악할 수있는 함수 서명. 서명은 인수가 복사 될 것임을 즉시 보여줍니다. 이렇게하면 호출자가 const std::string&
참조가 데이터 멤버로 저장되어 나중에 매달려 있는 참조가 될 수 있는지 궁금해하지 않아도됩니다. 그리고 rvalue가 함수에 전달 될 때 불필요한 복사를 피하기 위해 std::string&& name
및 const std::string&
인수 를 오버로드 할 필요가 없습니다 . lvalue 전달
std::string nameString("Alex");
Creature c(nameString);
인수를 값으로받는 함수에 대해 하나의 복사본과 하나의 이동 구성이 발생합니다. 동일한 함수에 rvalue 전달
std::string nameString("Alex");
Creature c(std::move(nameString));
두 개의 이동 구성이 발생합니다. 반대로 함수 매개 변수가 const std::string&
이면 rvalue 인수를 전달하더라도 항상 복사본이 있습니다. 이것은 인수 유형이 이동 생성에 저렴하다면 분명히 이점입니다 (이 경우 std::string
).
그러나 고려해야 할 단점이 있습니다. 함수 인수를 다른 변수에 할당하는 함수에 대해서는 추론이 작동하지 않습니다 (초기화하는 대신).
void setName(std::string name)
{
m_name = std::move(name);
}
m_name
재 할당되기 전에 참조 하는 리소스의 할당이 해제됩니다 . 나는 Effective Modern C ++의 Item 41 과이 질문을 읽는 것을 추천 합니다.
/* (0) */
Creature(const std::string &name) : m_name{name} { }
전달 된 lvalue는에 바인딩 된
name
다음에 복사 됩니다m_name
.A passed rvalue binds to
name
, then is copied intom_name
.
/* (1) */
Creature(std::string name) : m_name{std::move(name)} { }
A passed lvalue is copied into
name
, then is moved intom_name
.A passed rvalue is moved into
name
, then is moved intom_name
.
/* (2) */
Creature(const std::string &name) : m_name{name} { }
Creature(std::string &&rname) : m_name{std::move(rname)} { }
A passed lvalue binds to
name
, then is copied intom_name
.A passed rvalue binds to
rname
, then is moved intom_name
.
As move operations are usually faster than copies, (1) is better than (0) if you pass a lot of temporaries. (2) is optimal in terms of copies/moves, but requires code repetition.
The code repetition can be avoided with perfect forwarding:
/* (3) */
template <typename T,
std::enable_if_t<
std::is_convertible_v<std::remove_cvref_t<T>, std::string>,
int> = 0
>
Creature(T&& name) : m_name{std::forward<T>(name)} { }
You might optionally want to constrain T
in order to restrict the domain of types that this constructor can be instantiated with (as shown above). C++20 aims to simplify this with Concepts.
In C++17, prvalues are affected by guaranteed copy elision, which - when applicable - will reduce the number of copies/moves when passing arguments to functions.
How you pass is not the only variable here, what you pass makes the big difference between the two.
In C++, we have all kinds of value categories and this "idiom" exists for cases where you pass in an rvalue (such as "Alex-string-literal-that-constructs-temporary-std::string"
or std::move(nameString)
), which results in 0 copies of std::string
being made (the type does not even have to be copy-constructible for rvalue arguments), and only uses std::string
's move constructor.
There are several disadvantages of pass-by-value-and-move approach over pass-by-(rv)reference:
- it causes 3 objects to be spawned instead of 2;
- passing an object by value may lead to extra stack overhead, because even regular string class is typically at least 3 or 4 times larger than a pointer;
- argument objects construction is going to be done on the caller side, causing code bloat;
'IT TIP' 카테고리의 다른 글
C ++ 11 : 가변 템플릿 함수 매개 변수의 수? (0) | 2020.10.19 |
---|---|
package.json을 통해 "전역"npm 종속성 설치 (0) | 2020.10.19 |
ReactJS : 컴포넌트 초기 상태를 소품으로 전달하는 이유는 무엇입니까? (0) | 2020.10.19 |
"메모리 부족"이 복구 가능한 오류입니까? (0) | 2020.10.19 |
SQL Server로 업데이트 선택 (0) | 2020.10.19 |