C++ 함수 매개변수·반환 규칙¶
입력·변경·결과를 호출부에 드러낸다. 스마트 포인터는 소유권 전달 규칙을 우선한다.
기본 선택¶
| 목적 | 기본 방식 | 강도 |
|---|---|---|
| 복사 비용이 작은 입력 | T 값 전달 |
권장 |
| 복사 비용이 큰 객체를 읽기만 함 | const T& |
권장 |
| 기존 객체를 읽고 수정 | T& |
권장 |
| 부재가 유효한 비소유 입력 | const T* 또는 T* |
권장 |
| 새 결과 하나 생성 | T 값 반환 |
권장 |
| 의미상 묶인 결과 여러 개 | 이름 있는 구조체 반환 | 권장 |
| 호출자 버퍼 재사용·외부 API | 계약을 명시한 출력 인자 허용 | 선택 |
입력과 입출력¶
전달 방식은 비용·의도로 판단하며 바이트 크기만으로 결정하지 않는다.
#include <string>
bool HasPrefix(
const std::string& text,
char prefix)
{
return !text.empty() && text.front() == prefix;
}
void AddBonus(
int& damage,
int bonusDamage)
{
damage += bonusDamage;
}
T&는 기존 객체 수정이지 출력 전용이 아니다. 포인터의 null 의미와 수명 계약을 명시한다.
필수: 입력 참조를 호출 뒤에 저장하려면 수명 계약을 둔다. 복사·이동 가능한 값을 항상 보관한다면 값 전달 후 이동 저장을 기본안으로 사용할 수 있다.
#include <string>
#include <utility>
class Player
{
public:
void SetName(std::string name)
{
mName = std::move(name);
}
private:
std::string mName;
};
조건부 저장, 버퍼 재사용, 비용이 큰 타입은 실제 호출 패턴에 따라 별도로 검토한다. 단순 입력에 관성적으로 T&&를 사용하지 않는다. 전달 템플릿의 T&&·std::forward는 별도 주제로 둔다.
반환값으로 결과를 표현한다¶
큰 객체도 값 반환을 우선하며 비용은 복사 생략·이동·호출 패턴으로 확인한다.
#include <vector>
std::vector<int> CreateScores(int count)
{
std::vector<int> scores(count, 0);
return scores;
}
이 예제는 count >= 0을 전제로 한다. 일반 지역 결과는 return result;로 반환하고 return std::move(result);를 기계적으로 추가하지 않는다. 값 반환 타입에 최상위 const를 붙이지 않는다. 참조로 반환하는 const T&와는 다른 규칙이다.
struct Size
{
int width;
int height;
};
Size GetDefaultSize()
{
return {1280, 720};
}
구조체와 pair·tuple의 선택¶
상태: 채택 · 적용 범위: 복합 결과의 반환형 선택 · 개정일: 2026-10-09
요소의 크기·개수나 함수 길이가 아닌 의미·순서로 선택한다.
| 반환 방식 | 사용하는 경우 | 강도·예외 |
|---|---|---|
| 이름 있는 구조체 | 결과들이 하나의 개념을 이루거나 필드 이름으로 의미를 설명해야 하는 경우 | 권장. 크기·범위·파싱 결과 등 |
std::pair |
두 독립적인 값을 간단히 묶으며 각 값의 의미와 순서가 명확한 경우 | 선택. 간단한 임시 결과·범용 조합 |
std::tuple |
여러 독립적인 값을 임시로 묶거나 범용·가변 템플릿에서 결과를 조합하는 경우 | 선택. 별도 개념을 나타내는 결과는 구조체 우선 |
| 표준·외부 API의 반환형 | 해당 API가 반환형을 지정한 경우 | 해당 계약 우선. 이름만 바꾸기 위한 래퍼는 필수가 아님 |
width·height처럼 하나의 개념인 결과는 위 Size로 표현한다. tuple 별칭만으로는 필드 의미가 드러나지 않는다.
tuple은 C++11, 구조체에도 가능한 structured binding은 C++17부터다.
const auto size = GetDefaultSize();
const int width = size.width;
// C++17: Size 구조체도 분해할 수 있다.
const auto [defaultWidth, defaultHeight] = GetDefaultSize();
structured binding은 필드 순서에 의존한다. 지역 이름이 반환 계약을 대신하지 않으므로 이름이 필요한 곳은 필드 접근을 유지한다. 성공·오류 계약은 결과 없음과 실패를 따른다.
구조체·tuple의 성능 우위를 가정하지 않고 타입·복사/이동·컴파일러·호출 패턴으로 검증한다.
출력 인자를 허용하는 경우¶
외부 API의 지정된 시그니처, 호출자가 마련한 버퍼의 재사용, 측정으로 확인된 비용 절감이 필요한 경우 출력 인자를 허용한다. 새 객체 하나를 반환할 수 있는 일반 함수의 기본형으로 삼지는 않는다.
필수: 입력과 출력이 섞이면 무엇을 읽고 무엇을 쓰는지 설명한다. 실패 시 출력 유지·초기화·부분 변경 중 어떤 동작인지 명시하고 구현과 일치시킨다.
#include <string>
/// @brief 경로에 해당하는 이름을 읽어 output에 기록한다.
/// @return 성공 시 true. 실패 시 false이며 output은 변경하지 않는다.
bool TryReadName(
const std::string& path,
std::string& output);
출력은 입력 뒤에 두는 것을 권장하며 외부 API 순서는 유지한다. 예시의 실패 계약은 실제 구현과 일치시켜야 한다.
결과 없음과 실패¶
- 반환할 데이터가 없는 명령은
void, 참·거짓 질의나 성공 여부만 필요한 연산은bool을 사용할 수 있다. - 데이터가 없을 수 있는 결과에는 C++17 이상에서
std::optional<T>를 고려한다. 오류 원인까지 담는 형식은 아니다. - 오류 원인이 필요한 연산은 결과 타입·오류 코드·예외 정책을 별도로 정한다.
std::expected를 사용한다면 C++23 및 도구 지원을 확인한다. 0,-1, 빈 문자열을 보편적인 실패값으로 삼지 않는다. 정상 결과와 구분되는지 먼저 확인한다.
이 절은 반환형 선택의 경계만 다룬다. 프로젝트 전체의 예외 허용 범위나 오류 처리 방식은 아직 확정하지 않는다.
반환값 무시와 nodiscard¶
권장: 결과 폐기가 잘못된 사용·중요한 누락인 API에 [[nodiscard]]를 붙인다(C++17). bool·Getter라는 이유로 일괄 적용하지 않는다.
실패 후 진행·종료·소비 데이터·계산값 등 후속 동작의 전제인지 판단한다. 내부에서 실패를 기록해도 호출자가 중단 여부를 정해야 하면 확인한다.
변수에 저장하는 것만으로 진단을 피할 수 있어 실제 검사를 보장하지 않는다. 적용 대상·버전은 attribute 초안을 참조한다.
의도적인 반환값 무시¶
상태: 채택 · 적용 범위: 결과를 사용하지 않아도 계약이 성립하는 호출부 · 개정일: 2026-10-09
- 필수: 우선 결과를 검사·사용·상위 호출자에게 전달해야 하는지 확인한다. 실패 후 진행 여부를 정해야 하는 호출에 경고 제거만을 위한 캐스트를 넣지 않는다.
- 권장: 반환값을 의도적으로 무시함을 표시할 때는
(void)Function();을 기본 표기로 사용한다. 결과를 버리는 이유가 검토상 중요한 호출에도 이 표기를 사용한다. - 선택: 결과 무시가 자연스럽고 계약상 문제가 없는 일반 호출은
Function();만 작성할 수 있다. 모든 비-void 호출에 별도 표기를 요구하지 않는다. - 필수:
[[nodiscard]]결과를 무시하는 허용된 예외는 명시적으로 표시한다. 결과 무시가 허용되는지를 먼저 판단하며, attribute가 없는 함수도 결과 확인이 필요한 계약이면 검사한다. - 필수: 실패 결과를 버리는 예외에는 실패를 어디서 보고하는지와 이후 어떻게 진행하는지 남긴다. 주변 계약에서 분명하면 같은 설명을 호출마다 반복할 필요는 없다.
- 선택: 프로젝트의 기존 스타일이나 도구 정책에 따라
static_cast<void>(Function());·std::ignore = Function();도 사용할 수 있다.std::ignore는<tuple>을 직접 포함하고 지원 라이브러리·분석 도구의 동작을 확인한다. 같은 프로젝트에서는 특별한 이유 없이 여러 표기를 섞지 않는 것을 권장한다.(void)를 수치·포인터의 C 스타일 캐스트 규칙과 구분한다.
// 종료 정리에서 완료를 시도한다. 실패는 함수 내부에서 보고하고 정리를 계속한다.
// 이 경로의 결과 폐기는 GPU 완료를 보장하지 않는다.
(void)WaitForGpu();
소멸자도 실패 무시의 안전성을 보장하지 않는다. 정리 계약을 확인하고 정상 실행·재사용이 완료 여부에 의존하면 결과를 검사한다.
void 캐스트는 표준의 진단 권고 예외이며 호출·부작용·예외는 유지된다. 미사용 변수·[[maybe_unused]]로 결과를 숨기는 방식을 기본으로 삼지 않는다.
확인 사례·기존 표기·추가 <tuple> 의존성을 고려해 (void)를 기본으로 선택했다. C4834의 std::ignore 권고와는 다르며 대체 표기는 허용한다. C26457 등 추가 진단은 별도 확인하고 설정은 변경하지 않는다.
관련 규칙과 예외¶
- 함수명은 명명 규칙, 여러 줄 매개변수와 닫는 괄호 배치는 포맷 규칙을 따른다.
- 매개변수의
const위치와 지역 변수 적용 기준은 const 사용 규칙에 둔다. - 포인터·참조·뷰 반환의 유효 기간과 비동기 저장은 소유권·수명 규칙에 둔다.
- 매개변수가 많거나 비슷한
bool·정수 인자가 반복되면 이름 있는 옵션 타입이나 열거형을 검토한다. 단순히 인자 개수를 줄이기 위한 무관한 값의 묶음은 피한다. - 오버라이드·콜백·C ABI 등 외부에서 지정한 함수 형태는 해당 계약을 우선한다.
관련 문서¶
근거와 출처¶
기존 자료(로컬 참고 자료)의 입력·반환·COM 출력을 참고하되 비용·의도로 구분하고 외부 API 출력을 일반 함수 기본으로 확대하지 않는다.
C++ Core Guidelines의 입력·입출력 구분과 값·구조체 반환 권고를 참고했다. 값 전달 후 저장, 출력 인자 배치와 실패 계약의 구체화는 이 저장소의 선택이다.
출처 확인일: 2026-09-12.
추가 확인일: 2026-10-09. 사용자 선택으로 pair·tuple 범위와 의도적 결과 무시를 구체화했다. 기존 자료의 예외를 공통 확대하지 않으며 구조체·nodiscard 권장은 유지한다.
- F.21 추가 비교: 2026-06-14 표기본, C++20 중심. 구조체 우선·독립 값·가변 템플릿의 tuple 사용을 참고하고 간단한 임시 결과 허용은 개인 선택으로 정했다.
- C++ 작업 초안 — dcl.attr.nodiscard: 경고 권고와 명시적인 void 변환 예외. 갱신되는 작업 초안이며 적용 버전은 C++17 기본형·C++20 이유 문자열로 구분한다.
- Microsoft C4834 — Remarks·Example: Visual Studio 문서 뷰
msvc-170. 경고·의도적 결과 무시와std::ignore권고, C26457 가능성을 확인했다.
채택일: 2026-09-12. 명시된 적용 범위 안에서 채택하며, 후속 정리 대상은 제외한다.
미결정 사항과 변경 기록¶
- 2026-10-16: 반복 설명·중복 예시·배경을 줄여 핵심 규칙과 예시 중심으로 정리했다. 규칙 강도·예외·버전과 채택·초안·미결정 상태는 유지했다.
프로젝트 오류 처리와 진단 설정은 별도 결정하며 보류·폐기로 확정한 항목은 없다.
- 2026-10-09: 사용자 선택으로 구조체·pair·tuple의 선택 기준과 의도적 결과 무시의
(void)권장 기본·표기 생략·대체 표기 허용 범위를 채택했다. 기존 nodiscard 기준과 진단의 한계를 설명하고 attribute 초안으로 연결했다. 오류 처리·진단 설정은 프로젝트별 결정으로 유지한다.