태그 보관물: stdvector

stdvector

C ++에서 std :: vector를 반환하는 효율적인 방법 vector를 반환 할 때 복사되는

함수에서 std :: vector를 반환 할 때 복사되는 데이터의 양과 std :: vector를 free-store (힙에)에 배치하고 대신 포인터를 반환하는 것이 최적화 될 것입니다.

std::vector *f()
{
  std::vector *result = new std::vector();
  /*
    Insert elements into result
  */
  return result;
} 

다음보다 더 효율적입니다.

std::vector f()
{
  std::vector result;
  /*
    Insert elements into result
  */
  return result;
} 

?



답변

C ++ 11에서는 이것이 선호되는 방법입니다.

std::vector<X> f();

즉, 값으로 반환합니다.

C ++ 11에서는 std::vector이동 시맨틱이 있습니다. 즉, 함수에 선언 된 로컬 벡터가 반환시 이동 되며 경우에 따라 컴파일러에서 이동조차 제거 할 수 있습니다.


답변

값으로 반환해야합니다.

이 표준에는 가치 반환의 효율성을 향상시키는 특정 기능이 있습니다. 이를 “복사 제거”라고하며 더 구체적으로이 경우에는 “명명 된 반환 값 최적화 (NRVO)”라고합니다.

컴파일러는 그것을 구현하지 않지만, 다시 컴파일러는하지 않습니다 인라인 기능을 구현하기 위해 (또는 전혀 최적화를 수행). 그러나 컴파일러가 최적화하지 않고 모든 심각한 컴파일러가 인라인 및 NRVO (및 기타 최적화)를 구현하면 표준 라이브러리의 성능이 상당히 떨어질 수 있습니다.

NRVO가 적용되면 다음 코드에 복사가 없습니다.

std::vector<int> f() {
    std::vector<int> result;
    ... populate the vector ...
    return result;
}

std::vector<int> myvec = f();

그러나 사용자는 이것을 원할 수 있습니다.

std::vector<int> myvec;
... some time later ...
myvec = f();

복사 제거는 초기화가 아닌 할당이기 때문에 여기서 복사를 방지하지 않습니다. 그러나 여전히 값으로 반환 해야 합니다. C ++ 11에서 할당은 “이동 의미론”이라는 다른 것에 의해 최적화됩니다. C ++ 03에서 위의 코드는 복사를 일으키며 이론적으로 는 최적화 프로그램이이를 피할 수 있지만 실제로는 너무 어렵습니다. 따라서 대신 myvec = f()C ++ 03에서 다음과 같이 작성해야합니다.

std::vector<int> myvec;
... some time later ...
f().swap(myvec);

사용자에게보다 유연한 인터페이스를 제공하는 또 다른 옵션이 있습니다.

template <typename OutputIterator> void f(OutputIterator it) {
    ... write elements to the iterator like this ...
    *it++ = 0;
    *it++ = 1;
}

그런 다음 기존 벡터 기반 인터페이스를 지원할 수도 있습니다.

std::vector<int> f() {
    std::vector<int> result;
    f(std::back_inserter(result));
    return result;
}

기존 코드가 고정 된 양보다 더 복잡한 방식으로 사용하는 경우 기존 코드보다 효율성 이 떨어질 수 있습니다 reserve(). 그러나 기존 코드가 기본적으로 push_back벡터를 반복적 으로 호출 하는 경우이 템플릿 기반 코드도 마찬가지로 좋습니다.


답변

RVO에 대한 답변을 게시 할 때입니다. 저도 …

값으로 객체를 반환하는 경우 컴파일러는 종종이를 최적화하여 두 번 생성되지 않도록합니다. 이는 함수에서 임시로 생성 한 다음 복사하는 것이 불필요하기 때문입니다. 이를 반환 값 최적화라고합니다. 생성 된 개체는 복사되는 대신 이동됩니다.


답변

일반적인 C ++ 11 이전 관용구는 채워지는 객체에 대한 참조를 전달하는 것입니다.

그런 다음 벡터 복사가 없습니다.

void f( std::vector & result )
{
  /*
    Insert elements into result
  */
} 


답변

컴파일러가 명명 된 반환 값 최적화 ( http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx )를 지원하는 경우 다음이없는 경우 벡터를 직접 반환 할 수 있습니다.

  1. 다른 명명 된 개체를 반환하는 다른 경로
  2. EH 상태가 도입 된 여러 반환 경로 (모든 경로에서 동일한 명명 된 개체가 반환되는 경우에도).
  3. 반환 된 명명 된 객체는 인라인 asm 블록에서 참조됩니다.

NRVO는 중복 복사 생성자 및 소멸자 호출을 최적화하여 전반적인 성능을 향상시킵니다.

귀하의 예에는 실제 차이가 없어야합니다.


답변

vector<string> getseq(char * db_file)

그리고 만약 당신이 그것을 main ()에 인쇄하고 싶다면 당신은 그것을 루프로해야합니다.

int main() {
     vector<string> str_vec = getseq(argv[1]);
     for(vector<string>::iterator it = str_vec.begin(); it != str_vec.end(); it++) {
         cout << *it << endl;
     }
}


답변

“값으로 반환”이 좋은 것처럼 오류로 이어질 수있는 종류의 코드입니다. 다음 프로그램을 고려하십시오.

    #include <string>
    #include <vector>
    #include <iostream>
    using namespace std;
    static std::vector<std::string> strings;
    std::vector<std::string> vecFunc(void) { return strings; };
    int main(int argc, char * argv[]){
      // set up the vector of strings to hold however
      // many strings the user provides on the command line
      for(int idx=1; (idx<argc); ++idx){
         strings.push_back(argv[idx]);
      }

      // now, iterate the strings and print them using the vector function
      // as accessor
      for(std::vector<std::string>::interator idx=vecFunc().begin(); (idx!=vecFunc().end()); ++idx){
         cout << "Addr: " << idx->c_str() << std::endl;
         cout << "Val:  " << *idx << std::endl;
      }
    return 0;
    };
  • Q : 위의 내용이 실행되면 어떻게 되나요? A : 코어 덤프.
  • Q : 컴파일러가 실수를 포착하지 못한 이유는 무엇입니까? A : 프로그램이 의미 론적으로는 아니지만 구문 론적으로 정확하기 때문입니다.
  • Q : 참조를 반환하도록 vecFunc ()를 수정하면 어떻게됩니까? A : 프로그램이 완료 될 때까지 실행되고 예상 결과가 생성됩니다.
  • Q : 차이점은 무엇입니까? A : 컴파일러는 익명 개체를 만들고 관리 할 필요가 없습니다. 프로그래머는 깨진 예제처럼 두 개의 다른 개체가 아닌 반복기와 끝점 결정에 정확히 하나의 개체를 사용하도록 컴파일러에 지시했습니다.

위의 잘못된 프로그램은 GNU g ++보고 옵션을 사용하더라도 오류가 없음을 나타냅니다. -Wall -Wextra -Weffc ++

값을 생성해야하는 경우 vecFunc ()를 두 번 호출하는 대신 다음이 작동합니다.

   std::vector<std::string> lclvec(vecFunc());
   for(std::vector<std::string>::iterator idx=lclvec.begin(); (idx!=lclvec.end()); ++idx)...

위의 코드는 루프를 반복하는 동안 익명의 객체를 생성하지 않지만 가능한 복사 작업이 필요합니다 (일부 상황에서 최적화 될 수 있음). 그러나 참조 메서드는 복사가 생성되지 않음을 보장합니다. 컴파일러가 믿으면 perform RVO는 당신이 할 수있는 가장 효율적인 코드를 만드는 것을 대체 할 수 없습니다. 컴파일러가 RVO를 수행 할 필요성을 의심 할 수 있다면, 당신은 게임보다 앞서 있습니다.