Program Club

C ++에서 std :: vector를 반환하는 효율적인 방법

proclub 2020. 11. 23. 20:24
반응형

C ++에서 std :: 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
  */
} 

If the compiler supports Named Return Value Optimization (http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx), you can directly return the vector provide that there is no:

  1. Different paths returning different named objects
  2. Multiple return paths (even if the same named object is returned on all paths) with EH states introduced.
  3. The named object returned is referenced in an inline asm block.

NRVO optimizes out the redundant copy constructor and destructor calls and thus improves overall performance.

There should be no real diff in your example.


vector<string> getseq(char * db_file)

And if you want to print it on main() you should do it in a loop.

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

Yes, return by value. The compiler can handle it automatically.


   vector<string> func1() const
   {
      vector<string> parts;
      return vector<string>(parts.begin(),parts.end()) ;
   } 


As nice as "return by value" might be, it's the kind of code that can lead one into error. Consider the following program:

    #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: What will happen when the above is executed? A: A coredump.
  • Q: Why didn't the compiler catch the mistake? A: Because the program is syntactically, although not semantically, correct.
  • Q: What happens if you modify vecFunc() to return a reference? A: The program runs to completion and produces the expected result.
  • Q: What is the difference? A: The compiler does not have to create and manage anonymous objects. The programmer has instructed the compiler to use exactly one object for the iterator and for endpoint determination, rather than two different objects as the broken example does.

The above erroneous program will indicate no errors even if one uses the GNU g++ reporting options -Wall -Wextra -Weffc++

If you must produce a value, then the following would work in place of calling vecFunc() twice:

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

The above also produces no anonymous objects during iteration of the loop, but requires a possible copy operation (which, as some note, might be optimized away under some circumstances. But the reference method guarantees that no copy will be produced. Believing the compiler will perform RVO is no substitute for trying to build the most efficient code you can. If you can moot the need for the compiler to do RVO, you are ahead of the game.

참고URL : https://stackoverflow.com/questions/15704565/efficient-way-to-return-a-stdvector-in-c

반응형