C++에서 클래스의 멤버 변수로 const int가 있다면, 이는 정수 상수가 아닌가요?

HDNua의 이미지

머리 회전 같은 것도 있고, 실제로 재미있는 질문도 여럿 있고 해서
네이버에서 지식인 놀이를 하고 다니는 편입니다.

이런 질문이 있어 답변을 달아드렸는데, 질문자 분께서 궁금한 게 많으신 모양이네요. ㅎㅎ
http://kin.naver.com/qna/detail.nhn?d1id=1&dirId=1040101&docId=161098449&page=1#answer1

제 생각대로 답변을 달아드렸습니다만, 혹 엉뚱한 소리를 하는 게 아닐까 걱정이 되기도 하고,
무엇보다 제가 궁금해졌습니다.

문제 상황은 이렇습니다.

#include <iostream>
using std::cout;
using std::cin;
using std::endl;
 
class Test
{
private:
    const int value;
 
public:
    Test(): value(0) {}
 
    void func()
    {
        int v = 5;
        switch (v)
        {
            case value:
                cout<<"Hi"<<endl;
                break;
        }
    }
};
 
int main(void)
{
    Test test;
    test.func();
 
    return 0;
}

case value: 부분에서 에러가 나는데, 에러 이유는 이렇다고 합니다.
Case value is not a constant expression(케이스에 들어간 값이 정수 상수식이 아닙니다)
제가 답변을 달아드렸을 때는, 컴파일러가 const int로 된 부분을 보면 이 값을 치환한다고 답변을 달아드렸는데
지금 멤버 이니셜라이저에서 이렇게 상수로 줘놓고도 안 되니 슬슬 헷갈리기 시작하네요.

언제나 좋은 답변 감사합니다.

P.S. Dennis Ritchie 씨의 The C Programming Language, Bjarne Stroustrup씨의 The C++ Programming Language를 보고 싶은데 가격이 굉장하네요..

empty2fill의 이미지

stackoverflow에 비슷한 질문이 있네요.

switch case 문에는 compile-time에 알수 있는 상수를 사용할 수 있는데요.

클래스의 const 맴버는 constant(상수)를 의미하는게 아니라 정확히는 read-only를 말하는 거라고 하네요.

그래서 "Case value is not a constant expression" 에러가 난거구요.

[참고]
http://stackoverflow.com/questions/8049834/case-expression-not-constant


아래와 같이 Test 클래스 생성시 변수로 초기화 하면 어떻게 될까요? 컴파일 타임에 Test 클래스의 value 값을 알 수 있을 까요?

#include <iostream>
#include <cstdlib>
using std::cout;
using std::cin;
using std::endl;
 
class Test
{
private:
    const int value;
 
public:
    Test(int v): value(v) {}
 
    void func()
    {
        int v = 5;
        switch (v)
        {
            case value:
                cout<<"Hi"<<endl;
                break;
        }
    }
};
 
int main(int argc, char* argv[])
{
    int v = atoi(argv[0]);
 
    Test test(v);
 
    test.func();
 
    return 0;
}
 

——
———
Life is a tragedy when seen in close-up, but a comedy in long-shot. - Chaplin, Charlie -

익명 사용자의 이미지

> 아래와 같이 Test 클래스 생성시 변수로 초기화 하면 어떻게 될까요? 컴파일 타임에 Test 클래스의 value 값을 알 수 있을 까요?

안됩니다. compile-time constant 는 말 그대로 컴파일시에 그 값이 정해져있어야 합니다. 예를 들면 이런 식이지요.

const int X = 10; //a compile time constant
switch(v) case X ...

enum { A, B, C};//compile time constants
switch(v) case A ...

하지만 쓰신 코드에서 value의 값은 argv[0] (아마도 argv[1]을 의도하셨을 것 같습니다.)로 부터 정해지고
이 값은 runtime에 프로그램을 실행시킬 때에 정해집니다. 고로 원하는 바를 이룰 수 없습니다.

empty2fill의 이미지

저도 명확히 알고 싶어서 ISO C++ 표준 문서를 다시 읽어 봤는데요. 능력 부족인지 못찾겠네요.
(왜 클래스 const 맴버 변수를 switch case문에 사용할 수 없는지)

언어 명세에 따라 불가능 한 것인지, 아니면 컴파일러 최적화에 따라 불가능 한 것인지 잘 모르겠네요.

클래스 const 맴버 변수를 '상수'로 초기화 했을때 컴파일러가 (마음만 먹으면) 최적화 할 수 도 있을 것 같은데 왜 안하는지 의문이구요.

클래스 const 맴버 변수를 switch case문에 사용할 수 없는지는 못 찾았지만,

const int X = 10; 를 switch case문에 사용할 수 있는 이유는 찾았습니다.

5.19 Constant expressions [expr.const]
1 In several places, C + + requires expressions that evaluate to an integral or enumeration constant: as array
bounds (8.3.4, 5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2),
as static member initializers (9.4.2), and as integral or enumeration non-type template arguments (14.3).

constant-expression:
conditional-expression

An integral constant-expression can involve only literals (2.13), enumerators, const variables or static
data members of integral or enumeration types initialized with constant expressions (8.5), non-type template
parameters of integral or enumeration types, and sizeof expressions.

integral constant-expression에 const variables를 사용할 수 있다고 나오네요.

그렇다면 최적화 문제는 아니라는 생각도 드네요. 어떠세요?

——
———
Life is a tragedy when seen in close-up, but a comedy in long-shot. - Chaplin, Charlie -

HDNua의 이미지

먼저 노고에 감사드립니다. ㅎㅎ

-----
(integral이 정수라는 뜻도 갖고 있나요?)
5.19절. 상수 표현식 [expr.const]
1. 몇몇 상황에서, C++에서는 정수 또는 열거형 상수식을 평가하는 표현식을 필요로 합니다. 이 경우는 배열의 크기(8.3.4, 5.3.4), switch-case 표현식(6.4.2), 비트 필드의 길이(9.6), 열거형 이니셜라이저(7.2), 정적 멤버 이니셜라이저(9.4.2) 그리고 정수 또는 열거형의 비타입 인수에 대한 경우입니다.

상수 표현식: 조건부 표현식
정수 상수 표현식은 리터럴(2.13), 열거형, 상수 표현식(8.5)으로 초기화된 정수 또는 열거형 타입의 상수 변수 또는 정적 데이터 멤버, 정수 또는 열거형의 비타입 인수, sizeof 연산자 표현식입니다.
-----
위에 제시된 상수 표현식 이외는 상수 표현식으로 인정하지 않겠다는 말일수도 있겠네요.
클래스의 const int 멤버는 '상수 표현식으로 초기화된 정수 또는 열거형 타입의 상수 변수'라는 항목에 속할 수도 있을 것 같은데 헷갈리네요.
오히려 언어 명세에 따라 가능해야 하는 것 아닌지.. 참. ㅎㅎ

아, 그리고 const int로 선언된 변수를 switch-case에 사용할 수 있는 이유는 여기에서도 찾을 수 있었습니다.
http://kldp.org/node/68231

다시 한 번 감사드려요.

저는 이렇게 생각했습니다.

ifree의 이미지

const int 를 컴파일러가 최적화한다는 것은 해당 변수에 메모리를 할당하지 않고 컴파일러가 처리한다는 것이지만, 그렇다고 해당 변수 자체가 상수 표현이 된다는 말은 아닙니다.
외부에서 이 변수 메모리를 참조하거나 하는 경우에는 최적화할 수 없기 때문에 const int 변수도 엄연히 정상적인 변수라고 할 수 있죠.

HDNua의 이미지

아! 외부에서 메모리를 참조하면 컴파일러가 최적화를 할 수 없기 때문에 발생한 문제인가요?

지금 main.cpp에 const int value = 10;으로 선언해주고
Test 클래스를 정의한 Test.h에 extern const int value;를 선언해줘봤습니다.
Test.cpp에 func()라는 전역 함수를 선언해서 switch-case에 넣어줬더니 상수식이 아니라고 하는군요.

멤버 변수이므로 어떤 방식으로든 외부에서 참조가 일어나기때문에
컴파일러가 최적화할 수 없어서 '정수 상수'는 되지 못하고 변수로 남아있다는 얘기를 하신 것 같습니다.
제가 맞게 이해한 건가요?

저는 이렇게 생각했습니다.

ifree의 이미지

최적화를 할 수 있다 없다를 말하는 것은 아니고,
최적화가 되어 메모리가 할당되지 않았다 하더라도 메모리 주소를 가지는 일반 변수와 똑같이 취급된다는 말이었습니다.
즉 컴파일 시에 값이 알려져 있지만 컴파일 타임 상수는 아니라는...

익명 사용자의 이미지

const int X = 10 는 컴파일 타임 상수가 맞습니다. 아래에도 썼지만 C/C++의 컴파일 단위는 모듈입니다.
const int X가 정의되어 있는 파일에서는 컴파일 시에 그 값을 알 수 있고 상수 입니다.
하지만 이를 참조하는 다른 파일에서는 그 값을 알지만 상수가 아닌 것이 아니고,
그냥 컴파일 시에 그 값을 모릅니다. 물론 상수도 아니구요.

익명 사용자의 이미지

좀 더 명확히 하자면 다른 모듈이 컴파일될 때에는 const int X로 선언되어 있으면 상수이구요. int X로 되어있으면 상수가 아닙니다.

HDNua의 이미지

답변 감사합니다.
컴파일 타임에 결정된 것과 런타임에 결정된 것을 확실하게 구분할 수 있어야 하겠네요..

저는 이렇게 생각했습니다.

DarkSide의 이미지

컴파일 타임 상수라는 것은 컴파일 시에만 사용되고 이후에 잊혀지는 상수를 말하는데, const int 로 선언된 변수는 이와는 다른 성질을 가집니다.
심지어는 이 변수에 메모리가 할당되는 경우도 있죠.
컴파일 타임 상수라기 보다는 읽기 전용 변수로 취급해야 하며 최적화되어 메모리 할당이 되지 않아도 마찬가지입니다.

익명 사용자의 이미지

> 컴파일 타임 상수라는 것은 컴파일 시에만 사용되고 이후에 잊혀지는 상수를 말하는데,

그렇지 않습니다. 런타임에도 그 값을 알아야합니다.

> const int 로 선언된 변수는 이와는 다른 성질을 가집니다.

혹시 const 클래스 멤버를 말씀하시는 것인가요? 물론 이는 컴파일 타임 상수가 아니지요.
앞의 글들을 읽어보시면 클래스 멤버가 아닌 const int를 이야기하고 있음을 아실 겁니다.
이는 컴파일 타임 상수입니다.

> 심지어는 이 변수에 메모리가 할당되는 경우도 있죠.

컴파일 타임 상수와 메모리 할당은 아무런 관련이 없습니다.

이미 표준의 일부분을 다른 분이 옮겨주셨습니다만, case에는 constant expression만 사용될 수 있고
constant expression은 메모리 할당이나 컴파일 이후에 접근 불가능함 등이 아니라 특정한 조건들을 열거해서 정의합니다.
그리고 그 조건들은 다름 아니라 "단지 컴파일 타임 (translation time)에 값을 알고 있고 런타임에 값이 변하지 않는다고 선언됨" 이 그 핵심입니다.
물론 C++의 특성상 "런타임에 값이 변하지 않음"을 완전히 보장할 수는 없습니다.
예를 들면 (non-static 클래스 멤버가 아닌) const int X가 그런 경우이지요. 그런 경우에도 일단 현재 컴파일되는 파일(translation unit)에서 const로 선언된 이상
컴파일 타임 상수로 취급되고 case에 사용할 수 있습니다.

(사실 표준에서는 compile-time constant 라는 말을 정의하지 않습니다.
하지만 현재 논의의 특성상 (switch 구문), 그리고 일반적으로 통용되는 의미상 표준이 정의하는 constant expression과 같은 뜻이라고 봐도 되겠지요.)

익명 사용자의 이미지

잘못 썻네요.
> constant expression은 메모리 할당이나 컴파일 이후에 접근 불가능함 등이 아니라 특정한 조건들을 열거해서 정의합니다. 그리고 그 조건들은

> constant expression은 메모리 할당이나 컴파일 이후에 접근 불가능함 등의 조건이 아니라 언어 요소들을 열거해서 정의합니다. 그리고 그 요소들은
으로 고칩니다.

ifree의 이미지

const int 로 선언된 변수가 constant expression 이 될 수 없는 이유는 이렇게 선언해도 나중에 포인터를 사용해서 변수값이 바뀔 가능성이 있기 때문입니다.

http://kldp.org/node/128220

반면에 static const 는 이런 가능성 조차 없기 때문에 완전한 상수 표현으로 쓸 수 있죠.

익명 사용자의 이미지

> const int 로 선언된 변수가 constant expression 이 될 수 없는 이유는 이렇게 선언해도 나중에 포인터를 사용해서 변수값이 바뀔 가능성이 있기 때문입니다.

그래도 constant expression입니다.
constant expression이라는 말 뜻을 헷갈리시는 것 같습니다.
constant expression은 "런타임에 그 값이 절대로 바뀔 수 없는 표현"이 아닙니다.
제가 쓴 글들을 잘 읽어보십시오. 표준을 읽어보셔도 금방 아실거구요.
constant expression이 필요한 곳에 사용해서 컴파일을 해보셔도 될거구요.

> 반면에 static const 는 이런 가능성 조차 없기 때문에 완전한 상수 표현으로 쓸 수 있죠.

그렇지 않습니다. 그런 보장은 없습니다.
static이든 아니든 const 변수의 값을 포인터를 통해 바꾸는 것은 그냥 결과가 정의되지 않을 뿐입니다.

익명 사용자의 이미지

복잡할 이유가 하나도 없습니다.
C/C++에서 if문으로도 할 수 있는 일을 switch문을 사용해서 하는 이유는 switch문은 jump table을 만들기 때문입니다.
jump table에 대해서는 http://stackoverflow.com/questions/48017/what-is-a-jump-table 를 참조하시면 될 것 같습니다.
간단히 이야기하자면 if..else 체인처럼 하나 하나 순서대로 조건식을 평가하지 않고,
switch(v)에서 v를 읽은 다음에 단순한 index연산을 거쳐서 한 번에 정해진 위치로 옮겨가서 코드를 읽어 실행하는 겁니다.
그렇게 하기 위해서는 컴파일 타임에 그 값을 알고 그 값이 프로그램이 실행되는 동안 변하지 않을 expression들만 switch(v) case X 에서 X자리에 올 수 있습니다.
표준이 열거하고 있는 것들은 그냥 컴파일 타임 상수가 될 수 있는 녀석들을 하나 하나 써준 것 뿐입니다.
아주 단순한 문제입니다. C/C++에서 컴파일 단위는 소스 파일 하나(모듈이라고 부르지요) 입니다.
다른 모듈에서 정의된 변수의 값은 컴파일 타임에 그 값을 알 수가 없고 switch에 쓸 수도 없습니다.

익명 사용자의 이미지

부연 설명하자면 다른 모듈에서 정의된 값은 컴파일 타임에 알 수 없고,
클래스 const 맴버 변수도 컴파일 타임에 그 값이 정해지지 않습니다.
이해를 정확히 하셨는지 문제 하나 내볼까요? static const 멤버 변수는 case에 사용할 수 있을까요?

HDNua의 이미지

해당 클래스가 정의된 파일을 포함하는 소스 파일이라면 static const 멤버 변수를 case에 쓸 수 있군요..

저는 이렇게 생각했습니다.

winner의 이미지

자, 자 우리 compiler 부터 upgrade 하자구요.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.