const-reference로 std :: function을 전달해야합니까?


141

내가 필요한 기능이 있다고 가정 해 봅시다 std::function.

void callFunction(std::function<void()> x)
{
    x();
}

x대신 const-reference를 전달해야합니까 ? :

void callFunction(const std::function<void()>& x)
{
    x();
}

이 질문에 대한 답변은 기능에 따라 어떻게 달라 집니까? 예를 들어 클래스 멤버 함수 또는 생성자를 std::function멤버 변수에 저장하거나 초기화하는 경우입니다.


1
아마 아닙니다. 확실하지는 않지만 const 참조로 고려할 수있는 가장 작은 크기 인을 (를) sizeof(std::function)초과하지 않을 것으로 예상 합니다 2 * sizeof(size_t).
매트 피터슨

12
@Mats : std::function래퍼 의 크기 가 복사의 복잡성만큼 중요 하지 않다고 생각 합니다. 딥 카피가 포함 된 경우 sizeof제안 보다 훨씬 비쌀 수 있습니다 .
벤 Voigt

당신 move은 기능을 해야합니까 ?
Yakk-Adam Nevraumont

operator()()이다 constCONST 참조가 작동해야하므로. 그러나 나는 std :: function을 사용한 적이 없다.
Neel Basu

@ Yakk 람다를 함수에 직접 전달합니다.
Sven Adbring

답변:


79

성능을 원하면 저장하는 경우 값을 기준으로 전달하십시오.

"UI 스레드에서 실행"이라는 기능이 있다고 가정하십시오.

std::future<void> run_in_ui_thread( std::function<void()> )

"ui"스레드에서 일부 코드를 실행 한 다음 future완료되면 신호를 보냅니다 . (UI 스레드가 UI 요소를 엉망으로 만드는 UI 프레임 워크에서 유용합니다)

고려중인 두 가지 서명이 있습니다.

std::future<void> run_in_ui_thread( std::function<void()> ) // (A)
std::future<void> run_in_ui_thread( std::function<void()> const& ) // (B)

이제 다음과 같이 사용할 것입니다.

run_in_ui_thread( [=]{
  // code goes here
} ).wait();

익명 클로저 (람다)를 생성하고 std::function 그것을 닫고, run_in_ui_thread함수에 전달한 다음 메인 스레드에서 실행이 끝날 때까지 기다립니다.

(A)의 경우, std::function는 람다에서 직접 생성 된 다음 run_in_ui_thread. 람다는move 들어가므로 std::function, 어떤 이동 가능한 상태라도 효율적으로 들어간다.

두 번째 경우, 임시 std::function가 생성되고 람다는 move그 다음에 임시로 생성됩니다.std::function 는에서 참조로 사용됩니다 run_in_ui_thread.

지금까지는 두 가지가 동일하게 수행됩니다. 를 제외하고 run_in_ui_thread함수 인수의 사본을 작성하여 ui 스레드로 보내 실행하십시오! (완료되기 전에 반환되므로 참조를 사용할 수는 없습니다). 사례 (A)의 경우, 우리는 단순히movestd::function 장기 저장에. (B)의 경우을 복사해야합니다 std::function.

그 가게는 가치를 더 잘 전달합니다. 의 사본을 저장할 가능성이 있으면 std::function값으로 전달하십시오. 그렇지 않으면, 어느 쪽의 방법은 대략 동등합니다 : 값을 기준으로 한 유일한 단점은 같은 부피가 큰 경우입니다std::function 를 가지고 다른 하위 메소드를 사용한 후에 하나의 하위 메소드를 갖는 경우입니다. 그것을 막는 것은 a move만큼 효율적 const&입니다.

이제 둘 사이에 다른 상태가 있습니다. std::function 있습니다.

std::function상점이 일부 객체를 저장 한다고 가정하십시오 .operator() const 하지만 는mutable 수정하는 데이터 멤버 (무례합니다!).

std::function<> const&경우mutable 수정 된 데이터 멤버는 함수 호출에서 전파됩니다. 이 std::function<>경우에는 그렇지 않습니다.

이것은 비교적 이상한 코너 사례입니다.

std::function무게가 가볍고 저렴하게 움직일 수있는 다른 유형과 같이 취급하려고 합니다. 이동이 저렴하고 복사 비용이 많이들 수 있습니다.


"저장하여 값으로 전달"의 의미 상 장점은 계약에 의해 함수가 인수의 주소를 전달할 수 없다는 것입니다. 그러나 "그것을 막는 것이 const &만큼 효율적일 것"이라는 것이 사실입니까? 나는 항상 복사 작업 비용에 이동 작업 비용을 더한 것을 본다. 지나 가면 const&복사 작업 비용 만 봅니다.
ceztko

2
@ceztko (A)와 (B)의 경우 모두 std::function람다에서 임시 가 생성됩니다. (A)에서 임시는에 대한 인수로 생략됩니다 run_in_ui_thread. (B)에서 상기 임시에 대한 참조는로 전달된다 run_in_ui_thread. 너무 오래 당신으로 std::function의가 임시직으로 람다에서 생성되며, 그 절을 보유하고 있습니다. 이전 단락은 std::function지속 되는 경우를 다룹니다 . 우리가하면 되지 , 저장, 단지 람다에서 생성 function const&function동일한 오버 헤드를 가지고있다.
Yakk-Adam Nevraumont

아, 알겠다! 이것은 물론 외부에서 일어나는 일에 달려 run_in_ui_thread()있습니다. "참조로 전달하지만 주소를 저장하지 않습니다"라는 서명 만 있습니까?
ceztko

@ceztko 아니요, 없습니다.
Yakk-Adam Nevraumont

1
@ Yakk-AdamNevraumont rvalue 심판을 통과하는 또 다른 옵션을 포함하는 것이 더 완벽한 경우 :std::future<void> run_in_ui_thread( std::function<void()>&& )
Pavel P

33

성능이 걱정되고 가상 멤버 함수를 정의하지 않은 경우에는 사용하지 않아야합니다. std::function 에는 전혀 합니다.

functor 유형을 템플릿 매개 변수로 만들면 std::functionfunctor 로직 인라이닝을 포함하여 보다 큰 최적화가 가능 합니다. 이러한 최적화의 효과는 전달 방법에 대한 copy-vs-indirection 문제보다 훨씬 클 것 std::function입니다.

더 빠름 :

template<typename Functor>
void callFunction(Functor&& x)
{
    x();
}

1
나는 실제로 성능에 대해 전혀 걱정하지 않습니다. 방금 const-references를 사용해야하는 곳이 일반적인 관행이라고 생각했습니다 (문자열과 벡터가 떠 오릅니다).
Sven Adbring

13
@Ben : 이것을 구현하는 가장 현대적인 히피 친화적 인 방법은 std::forward<Functor>(x)();"유니버설"참조이기 때문에 functor의 가치 범주를 보존하기 위해 를 사용 하는 것입니다. 그러나 99 %의 경우에는 차이가 없습니다.
GManNickG 2009 년

1
@ Ben Voigt 그래서 귀하의 경우, 이동으로 함수를 호출 하시겠습니까? callFunction(std::move(myFunctor));
arias_JC

2
@arias_JC : 매개 변수가 람다 인 경우 이미 rvalue입니다. lvalue가 std::move있으면 더 이상 다른 방법으로 필요하지 않은 경우 사용하거나 기존 객체에서 벗어나고 싶지 않은 경우 직접 전달할 수 있습니다. 참조 축소 규칙 callFunction<T&>()은 유형 T&이 아닌 매개 변수를 갖도록합니다 T&&.
벤 Voigt

1
@BoltzmannBrain : 함수가 한 번만 호출 될 때 가장 간단한 경우에만 유효하기 때문에 변경하지 않기로 선택했습니다. 제 대답은 "함수 객체를 어떻게 전달해야합니까?"라는 질문에 대한 것입니다. 무조건 그 functor를 정확히 한 번만 호출하는 것 외에는 아무것도하지 않는 함수에 국한되지 않습니다.
Ben Voigt

25

C ++ 11에서와 마찬가지로 value / reference / const-reference로 전달하는 것은 인수로 수행하는 작업에 따라 다릅니다. std::function다르지 않습니다.

값으로 전달 하면 인수를 변수 (일반적으로 클래스의 멤버 변수)로 이동할 수 있습니다.

struct Foo {
    Foo(Object o) : m_o(std::move(o)) {}

    Object m_o;
};

함수가 인수를 옮길 것임을 알면 이것이 최선의 해결책입니다.이 방법으로 사용자는 함수 호출 방법을 제어 할 수 있습니다.

Foo f1{Object()};               // move the temporary, followed by a move in the constructor
Foo f2{some_object};            // copy the object, followed by a move in the constructor
Foo f3{std::move(some_object)}; // move the object, followed by a move in the constructor

나는 당신이 이미 (비) const-references의 의미를 알고 있다고 생각하므로 요점을 다루지 않을 것입니다. 이것에 대해 더 많은 설명을 추가 해야하는 경우 물어보십시오. 업데이트합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.