함수에서 '벡터'를 반환해도되는 이유는 무엇입니까?


108

이 코드를 고려하십시오. 이 유형의 코드를 여러 번 보았습니다. words로컬 벡터입니다. 함수에서 어떻게 반환 할 수 있습니까?

죽지 않을 것이라고 보장 할 수 있습니까?

 std::vector<std::string> read_file(const std::string& path)
 {
    std::ifstream file("E:\\names.txt");

    if (!file.is_open())
    {
        std::cerr << "Unable to open file" << "\n";
        std::exit(-1);
    }

    std::vector<string> words;//this vector will be returned
    std::string token;

    while (std::getline(file, token, ','))
    {
        words.push_back(token);
    }

    return words;
}

18
돌아올 때 복사됩니다.
songyuanyao

6
아무도 보장 .. 그것은 죽지 만이 복사 된 후.
Maroun 2014 년

7
함수가 참조를 반환하는 경우에만 문제가 있습니다.std::vector<std::string>&
Caduchon

14
@songyuanyao no, 이동됩니다.
rightfold

15
@songyuanyao 네. C ++ 11은 현재 표준이므로 C ++ 11은 C ++입니다.
rightfold

답변:


68

죽지 않을 것이라고 보장 할 수 있습니까?

참조가 반환되지 않는 한 그렇게해도 괜찮습니다. words결과를받는 변수로 이동합니다.

지역 변수가 범위를 벗어납니다. 이동 (또는 복사) 후.


2
그러나 1000 개의 항목을 보유 할 수있는 벡터에 대해 효율적이거나 성능 문제가 있습니까?
zar

@zadane 이것이 문제입니까? 또한 실제로 반환 값의 복사본을 사용하지 않는 이동에 대해 언급 했습니다 (적어도 현재 표준에서는 사용 가능).
πάντα ῥεῖ 2015

2
질문에있는 것은 아니지만 그 관점에서 독립적으로 답을 찾고있었습니다. 나는 내 질문을 게시하는지 모르겠습니다. 그들은 이것의 중복으로 표시 할 것입니다. :)
zar

@zadane "나는 그들이 이것의 중복으로 표시 할 것이 두렵다"가 될 수 있습니다. 그냥 한 번 봐 가지고 이상이 대답을 투표 . 이전 구현의 경우에도 걱정할 필요가 없습니다. 어쨌든 해당 컴파일러에 의해 대부분 올바르게 최적화됩니다.
πάντα ῥεῖ 2015

107

C ++ 11 이전 :

함수는 지역 변수를 반환하지 않고 복사본을 반환합니다. 그러나 컴파일러는 실제 복사 작업이 수행되지 않는 최적화를 수행 할 수 있습니다.

자세한 내용은 이 질문 및 답변 을 참조하십시오.

C ++ 11 :

함수는 값을 이동합니다. 자세한 내용은 이 답변 을 참조 하십시오.


2
복사되지 않고 이동됩니다. 이것은 보장됩니다.
rightfold

1
C ++ 10에도 적용됩니까?
Tim Meyer

28
C ++ 10과 같은 것은 없습니다.
rightfold

C ++ 03에는 이동 의미가 없었지만 (하지만 복사가 제거되었을 수 있음) C ++는 C ++ 11이고 질문은 C ++에 관한 것입니다.
rightfold

19
C ++ 11 전용 질문에 대한 별도의 태그가 있습니다. 우리 중 많은 사람들, 특히 대기업의 프로그래머는 아직 C ++ 11을 완전히 지원하지 않는 컴파일러에 집착하고 있습니다. 두 표준 모두에 대해 정확하도록 질문을 업데이트했습니다.
Tim Meyer

26

함수에서 배열을 반환하는 것이 허용되지 않거나 적어도 예상대로 작동하지 않는다는 C (및 C ++)의 문제를 언급하고 있다고 생각합니다. 이것은 배열 반환이 간단한 형식) 스택의 실제 배열에 대한 포인터를 반환 한 다음 함수가 반환 될 때 즉시 제거됩니다.

그러나이 경우 std::vector에는는 클래스이고 구조체와 같은 클래스는 호출자 컨텍스트에 복사 될 수 있기 때문에 작동합니다 . [사실, 대부분의 컴파일러는 "반환 값 최적화"라는 것을 사용하여이 특정 유형의 복사를 최적화합니다. 특히 함수에서 반환 될 때 큰 객체를 복사하지 않도록하기 위해 도입 된 것이지만 이는 최적화이며 프로그래머의 관점에서 보면 객체에 대해 할당 생성자가 호출 된 것처럼 동작]

반환하는 함수 내에있는 무언가에 대한 포인터 나 참조를 반환하지 않는 한 괜찮습니다.


13

동작을 잘 이해하기 위해 다음 코드를 실행할 수 있습니다.

#include <iostream>

class MyClass
{
  public:
    MyClass() { std::cout << "run constructor MyClass::MyClass()" << std::endl; }
    ~MyClass() { std::cout << "run destructor MyClass::~MyClass()" << std::endl; }
    MyClass(const MyClass& x) { std::cout << "run copy constructor MyClass::MyClass(const MyClass&)" << std::endl; }
    MyClass& operator = (const MyClass& x) { std::cout << "run assignation MyClass::operator=(const MyClass&)" << std::endl; }
};

MyClass my_function()
{
  std::cout << "run my_function()" << std::endl;
  MyClass a;
  std::cout << "my_function is going to return a..." << std::endl;
  return a;
}

int main(int argc, char** argv)
{
  MyClass b = my_function();

  MyClass c;
  c = my_function();

  return 0;
}

출력은 다음과 같습니다.

run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run constructor MyClass::MyClass()
run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run assignation MyClass::operator=(const MyClass&)
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()

이 예제는 C ++ 03 컨텍스트로 제공되었으며 C ++> = 11에 대해 개선 될 수 있습니다.


1
이 예제는 복사 생성자와 복사 할당 연산자뿐만 아니라 이동 생성자와 이동 할당 연산자도 포함하면 더 완벽합니다. (이동 기능이 없으면 복사 기능이 대신 사용됩니다.)
Some Guy

@SomeGuy 동의하지만 C ++ 11을 사용하지 않습니다. 내가없는 지식을 제공 할 수 없습니다. 메모를 추가합니다. C ++> = 11에 대한 답변을 자유롭게 추가하십시오. :-)
Caduchon

-5

동의하지 않으며 다음 을 반환 하지 않는 것이 좋습니다vector .

vector <double> vectorial(vector <double> a, vector <double> b)
{
    vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
    return c;
}

이것은 훨씬 더 빠릅니다.

void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
    c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}

릴리스 모드에서 다음 결과로 Visual Studio 2017에서 테스트했습니다.

참조에 의한 8.01 MOP
5.09 MOP 반환 벡터

디버그 모드에서는 상황이 훨씬 더 나빠집니다.

0.053 MOPS (참조 기준)
0.034 MOP (반환 벡터 기준)


-10

이것은 실제로 디자인의 실패입니다. 상대적으로 사소하지 않은 것에 대해 원시적이지 않은 것에 대해 리턴 값을 사용해서는 안됩니다.

이상적인 솔루션은 참조 / 포인터에 대한 결정과 설명 자로 "const \ 'y \'ness"를 적절하게 사용하여 반환 매개 변수를 통해 구현되어야합니다.

또한 C와 C ++의 배열에있는 레이블은 사실상 포인터이고 구독은 사실상 오프셋 또는 추가 기호라는 것을 알아야합니다.

따라서 레이블 또는 ptr array_ptr === 배열 레이블은 따라서 foo [offset]을 반환하는 것은 실제로 메모리 포인터 위치의 반환 요소 foo + 유형 반환 유형의 오프셋을 말합니다.


5
..........뭐. 당신이 "설계 실패"와 같은 비난을 던질 자격이 없다는 것이 분명해 보입니다. 사실, RVO 및 이동 작업에 의한 가치 의미론의 홍보 는 현대 C ++ 스타일 주요 성공 사례 중 하나 입니다. 그러나 당신은 원시 배열과 포인터에 대해 생각하고있는 것처럼 보이므로 이해하지 못할 것입니다.
underscore_d
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.