살군의 보조기억 장치

Another memory device…

Archive for March 2014

OpenCV Mat 클래스 분석#2 (예제코드)

with one comment

아마도 이 예제코드가 많이 도움이 될 것 같아서 바로 넣어둔다. 최초의 matrix 선언에서 type 을 바꿔가면서 실행해보면 각 함수, 멤버변수가 의미하는 바를 쉽게 확인할 수 있을 것이다. 아래글에서 언급한 내용을 확인하는데 도움이 될 것이다.


#include <stdio.h>
#include <iostream>
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/flann/miniflann.hpp"
#include <direct.h>

using namespace cv; // all the new API is put into "cv" namespace. Export its content
using namespace std;
using namespace cv::flann;


string depth2str(int depth) {
  string r;

  switch ( depth ) {
    case CV_8U:  r = "8U"; break;
    case CV_8S:  r = "8S"; break;
    case CV_16U: r = "16U"; break;
    case CV_16S: r = "16S"; break;
    case CV_32S: r = "32S"; break;
    case CV_32F: r = "32F"; break;
    case CV_64F: r = "64F"; break;
    default:     r = "User"; break;
  }

  return r;
}

string type2str(int type) {
  string r;

  uchar depth = type & CV_MAT_DEPTH_MASK;
  uchar chans = 1 + (type >> CV_CN_SHIFT);

  r = depth2str(depth);
  r += "C";
  r += (chans+'0');

  return r;
}

int main( int argc, char** argv )
{
    const int sz[] = {4,5,6};
    Mat M = Mat(3, sz, CV_16UC1);
    cv::randu(M, Scalar::all(0), Scalar::all(255));
    
    cout << "Mat::elemSize() = " << M.elemSize() << " Byte: same as sizeof(depth) * channels" << endl;
    cout << "Mat::elemSize1() = " << M.elemSize1() << " Byte: same as sizeof(depth)" << endl;
    cout << "Mat::depth() = " << depth2str(M.depth()) << endl;
    cout << "Mat::channels() = " << M.channels() << endl;
    cout << "Mat::type() = " << type2str(M.type()) << " : including both depth and channels, defined type number=" << M.type() << endl;
    cout << "Mat::dimension = " << M.dims << " --> rows = " << M.rows << ", cols = " << M.cols << endl;

    cout << endl << "\t\t|  Size[-1] = " << M.size[-1] << endl;
    for (int i=0; i<M.dims; i++) {
        cout << "Step[" << i << "] = " << M.step[i] << "\t|  Size[" << i << "] = " << M.size[i] << endl;
    }

    cout << endl;
    cout << "Mat::step1() = " << M.step1() << ": normalized step. matrix step divided by Mat::elemSize1()" << endl;
    cout << endl;

    return 0;
}

위 코드의 결과값은…

Mat::elemSize() = 2 Byte: same as sizeof(depth) * channels
Mat::elemSize1() = 2 Byte: same as sizeof(depth)
Mat::depth() = 16U
Mat::channels() = 1
Mat::type() = 16UC1 : including both depth and channels, defined type number=2
Mat::dimension = 3 –> rows = -1, cols = -1

                      | Size[-1] = 3
Step[0] = 60   | Size[0] = 4
Step[1] = 12   | Size[1] = 5
Step[2] = 2     | Size[2] = 6

Mat::step1() = 30: normalized step. matrix step divided by Mat::elemSize1()

참고
Advertisements

Written by gomiski

2014/03/13 at 5:23 am

OpenCV Mat 클래스 분석#1

with one comment

뭔가 시리즈물 글은 좋아하지 않느데… 이건 너무 거대한 거라 하나씩 끊고 가지 않으면 답이 없을 것 같아서 처음으로 시리즈를 시작한다. 물론 #2가 언제 나올지는 나도 모른다는게 함정. 지난번 글에서 이제는 코딩을 하면 될꺼라고 생각했는데 실수였다. N-dim matrix 내부를 볼려면 cv::Mat 클래스의 내용을 모르면 어찌 할 수가 없다. 당연히 내가 contribution 하려는 부분에도 문제가 생기는 거고. Matrix 출력을 matlab 처럼 하겠다는건데 내용물을 볼 수 없으면 시작도 못하는거니 말이다.

opencv 에서 cv::Mat 는 제일 기초가 되는 자료구조이다. 메뉴얼도 잘 되있어 opencv doc 페이지를 참조하면 쉽게 사용할 수 있다. 게다가 2-dimension + multi-channel 부분은 손댈 여지가 없을 정도로 잘 구현이 되어있다. 그러나 이전글에서 봤듯이, 3-dimension 이상은 matrix 의 내용을 쉽게 볼 수가 없다.

이 문제를 해결하기 위해, 우선 cv::Mat 내부를 살펴보자.

class CV_EXPORTS Mat
{
	// constructors & destructors
	// tons of overloading functions
	// ...

    enum { MAGIC_VAL=0x42FF0000, AUTO_STEP=0, CONTINUOUS_FLAG=CV_MAT_CONT_FLAG, SUBMATRIX_FLAG=CV_SUBMAT_FLAG };
	
    bool isContinuous() const;         //! returns true iff the matrix data is continuous (i.e. when there are no gaps between successive rows). similar to CV_IS_MAT_CONT(cvmat->type)
    bool isSubmatrix() const;          //! returns true if the matrix is a submatrix of another matrix
    
    size_t elemSize() const;           //! returns element size in bytes, similar to CV_ELEM_SIZE(cvmat->type)
    size_t elemSize1() const;          //! returns the size of element channel in bytes.
    size_t step1(int i=0) const;       //! returns step/elemSize1()
	
    int type() const;                  //! returns element type, similar to CV_MAT_TYPE(cvmat->type)
    int depth() const;                 //! returns element type, similar to CV_MAT_DEPTH(cvmat->type)
    int channels() const;              //! returns element type, similar to CV_MAT_CN(cvmat->type)
    bool empty() const;                //! returns true if matrix data is NULL
    
    size_t total() const;              //! returns the total number of matrix elements
    
    int flags;                    //! includes several bit-fields: - the magic signature, - continuity flag, - depth, - number of channels

    int dims;                     //! the matrix dimensionality, >= 2   
    int rows, cols;               //! the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
    uchar* data;                  //! pointer to the data

    int* refcount;                //! pointer to the reference counter; when matrix points to user-allocated data, the pointer is NULL
    uchar* datastart;             //! helper fields used in locateROI and adjustROI
    uchar* dataend;
    uchar* datalimit;

    MatAllocator* allocator;      //! custom allocator

    struct CV_EXPORTS MSize
    {
        //...
        int* p;
    };

    struct CV_EXPORTS MStep
    {
        //...
        size_t* p;
        size_t buf[2];
    };

    MSize size;
    MStep step;

    //...
};

이렇게 길게 소스코드를 늘어놓고 보니, 필수요소는 생각보다 적다. 우선, line:16~18 은 matrix 의 type, depth, channel 을 얻는것이다. 예를들어 matrix 선언시에 CV_16UC3 이라고 정의를 했다면, channel=3, depth=16U 가 되고 type 은 channel과 depth 정보를 모두 반환한다. 자세한 내용은 여기를 참조하자.

Line:25~26 은 matrix 의 행rows과 열cols 그리고 dimension 정보를 저장하고 있다.
Line:27 은 실제 matrix 의 데이터가 저장되는 곳을 지시하는 포인터이다.

마지막으로 line:49~50 은 matrix 의 부가정보를 기록하는 곳이다. matrix 는 초기화시에 size, step 에 대해 정의를 한다. 예를들어, CV_8UC1 에 3 x 4 인 2D matrix + 1-channel 을 정의했을때, step 은 행row과 열col에서 사용하는 Byte 수를 저장하고, size 는 행과 열의 수를 저장한다. 자세한 예제는 다른글을 참고하자.

여기서 언급하고 싶은 부분은 negative index 를 가지는 포인터 사용에 대한 것이다. size 와 step 을 초기화하는 아래 코드를 보면

static inline void setSize( Mat& m, int _dims, const int* _sz,
                            const size_t* _steps, bool autoSteps=false )
{
    CV_Assert( 0 <= _dims && _dims <= CV_MAX_DIM );
    if( m.dims != _dims )
    {
        if( m.step.p != m.step.buf )
        {
            fastFree(m.step.p);
            m.step.p = m.step.buf;
            m.size.p = &m.rows;
        }
        if( _dims > 2 )
        {
            m.step.p = (size_t*)fastMalloc(_dims*sizeof(m.step.p[0]) + (_dims+1)*sizeof(m.size.p[0]));
            m.size.p = (int*)(m.step.p + _dims) + 1;
            m.size.p[-1] = _dims;
            m.rows = m.cols = -1;
        }
    }
    //...
}

line:17 에서 어마어마한 사용법을 볼 수 있다. 세상에나! 인덱스가 음수다. 이게 가능한가? 첨 보면 아주아주 당황스럽다. 기본적으로 배열의 인덱스는 0 부터 시작하고 음수가 될 수 없다라고 알고 있을 것이다. 그러나 실상은 그렇지 않다. 즉, 음수가 가능하다. 단, 미리 정의된 배열의 범위안에서 사용이 가능한 것이다.

That is correct. From C99 §6.5.2.1/2:

The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))).

There’s no magic. It’s a 1-1 equivalence. As always when dereferencing a pointer (*), you need to be sure it’s pointing to a valid address.

하나하나 정리를 하고 싶은데 시간이 없네. 위의 코드에서 step 과 size 의 핵심은 메모리 영역을 공유하면서 포인터로 지시하는 위치만 다르다는 것이다.. 할당된 메모리의 시작위치는 step.p 가 가르키고 있고, dim + 1 만큼 이동한 위치를 size.p 가 지시한다. 예를들어, 2D matrix 를 선언했다고 할 때, size 와 step 이 사용하는 메모리는 2 * sizeof(m.step.p[0]) + (2 + 1) * sizeof(m.size.p[0])만큼 할당이 된다. m.step.p[0] 과 m.size.p[0] 은 unsigned int 이므로 sizeof(m.step.p[0]) 은 4 가 된다. 그러므로 총 메모리 할당량은 20 Byte 가 된다.

아래 메모리 모형은 1 칸이 4 Byte 라고 볼 때,

| a | b | c | d | e | …

m.step.p = addr(a)
m.size.p = addr(d)
m.size.p – 1 = addr(c) —> m.size.p[-1] = value(c)

가 되므로 유효한 표현식이 되는 것이다. 이 내용을 확인하고 싶다면 여기 참조.

나머지 내용은 다음 기회에.

참고

Written by gomiski

2014/03/13 at 5:04 am

OpenCV Data Type 정리

leave a comment »

Opencv 의 기본 데이터 타입이 자꾸 햇갈려서 정리한다. 기본적으로는 아래와 같다.

  • CV_8U : 8-bit unsigned integer: uchar ( 0..255 )
  • CV_8S : 8-bit signed integer: schar ( -128..127 )
  • CV_16U : 16-bit unsigned integer: ushort ( 0..65535 )
  • CV_16S : 16-bit signed integer: short ( -32768..32767 )
  • CV_32S : 32-bit signed integer: int ( -2147483648..2147483647 )
  • CV_32F : 32-bit floating-point number: float ( -FLT_MAX..FLT_MAX, INF, NAN )
  • CV_64F : 64-bit floating-point number: double ( -DBL_MAX..DBL_MAX, INF, NAN )

그치만 실제로 사용하는 것은 멀티채널의 배열을 정의하는 경우가 많으므로, 아래와 같이 사용하는 경우가 많다.

Multi-channel (n-channel) types can be specified using the following options:

  • CV_8UC1 … CV_64FC4 constants (for a number of channels from 1 to 4)
  • CV_8UC(n) … CV_64FC(n) or CV_MAKETYPE(CV_8U, n) … CV_MAKETYPE(CV_64F, n) macros when the number of channels is more than 4 or unknown at the compilation time.
Note: CV_32FC1 == CV_32F, CV_32FC2 == CV_32FC(2) == CV_MAKETYPE(CV_32F, 2), and CV_MAKETYPE(depth, n) == ((x&7)<<3) + (n-1). This means that the constant type is formed from the depth, taking the lowest 3 bits, and the number of channels minus 1, taking the next log2(CV_CN_MAX) bits.
참고

Written by gomiski

2014/03/11 at 8:37 am

Posted in C++, General, Lecture, opencv

Tagged with , , , ,

opencv output formatter 처리 방식

with one comment

자… 내가 처음 타겟으로 잡았던 output bug 에 가까이 가고 있다. 이제 formatter 내용에 대해 분석하기까지 왔다. 물론 여기서도 갈 길은 멀지만, 그래도 처음의 opencv 시작하기 부분을 보면, 완전 헤메고 있었는데 벌써 여기까지 왔나 싶기도 하다. 게다가 아까 한 질문에 누군가 답도 해줬다. opencv 의 output formatter 를 설명하는 예제를 한번 보자.

int main(int,char**)
{
    help();
    Mat I = Mat::eye(4, 4, CV_64F);
    I.at(1,1) = CV_PI;
    cout << "I = " << I << ";" << endl;

    Mat r = Mat(10, 3, CV_8UC3);
    randu(r, Scalar::all(0), Scalar::all(255));

    cout << "r (default) = " << r << ";" << endl << endl;
//    cout << "r (mablab) = " << format(r, "matlab") << ";" << endl << endl;
    cout << "r (python) = " << format(r,"python") << ";" << endl << endl;
    cout << "r (numpy) = " << format(r,"numpy") << ";" << endl << endl;
    cout << "r (csv) = " << format(r,"csv") << ";" << endl << endl;
    cout << "r (c) = " << format(r,"C") << ";" << endl << endl;
    ...

내가 관심있는 부분만 발췌해왔다. 전체 소스코드는 여기에서 볼 수 있다. line:11 을 보면, ostream 을 오버로딩overloading한 matrix 출력이다. 그냥 cout 에 << 연산자로 집어넣으면 바로 들어간다. 내가 살펴보려는 것이 바로 이 메커니즘이다. 저렇게 바로 넣으면 잘 들어가네. 어? 어떻게 저렇게 하지? 궁금하지 아니한가? 세상은 좋아졌고 모든 소스를 볼 수 있다. 이게 구현된 소스 내용을 살펴보자.

static inline std::ostream& operator << (std::ostream& out, const Mat& mtx) {
    Formatter::get()->write(out, mtx);
    return out;
}

class CV_EXPORTS Formatter
{
public:
    virtual ~Formatter() {}
    virtual void write(std::ostream& out, const Mat& m, const int* params=0, int nparams=0) const = 0;
    virtual void write(std::ostream& out, const void* data, int nelems, int type, const int* params=0, int nparams=0) const = 0;
    static const Formatter* get(const char* fmt="");
    static const Formatter* setDefault(const Formatter* fmt);
};

ostream 연산자 오버로딩은 거의 위와 같이 고정된 형식으로 사용한다. 자세한 내용은 다음 기회에… 간단한 함수다. line:10 과 line:11 을 보면, 이상한 문법이 보인다. 함수 뒤에 “= 0” 을 붙어놨다. 이것은 이 함수가 순수가상함수pure virtual function이라는 뜻이다. 다시말해, 이 함수는 현재 정의되어 있지않고, 누군가 상속하는 클래스에서 정의할 것이다 라고 명시적으로 알려주는 것이다. 하나 더 짚고 넘어갈 부분은 여기서 “= 0” 이라고 붙인 것이 NULL을 의미하는 것은 아니다. 자세한 사항은 여기를 참고하고 더 궁금하다면 vtable 로 검색하면 자세한 내용을 찾아볼 수 있다.

위의 코드를 따라가면, line:2 에서 Formatter::get() 으로 Formatter* 를 받아와서 line:10 의 write() 를 실행시킨다. Formatter::get() 으로 실제 어떤 포멧으로 출력할지를 정의한 하위클래스를 결정해서 이 하위 클래스에서 재정의한 write()를 수행하게 된다. 제일 상단에 있는 코드의 line:11 을 수행하면 실제로 Formatter::get() 에서는 MatlabFormatter 를 받아오게 된다.

그럼 순수가상함수가 실제 구현된 MatlabFormatter 부분을 살펴보자.

class MatlabFormatter : public Formatter
{
public:
    virtual ~MatlabFormatter() {}
    void write(std::ostream& out, const Mat& m, const int*, int) const
    {
        out << "[";
        writeMat(out, m, ';', ' ', m.cols == 1);
        out << "]";
    }

    void write(std::ostream& out, const void* data, int nelems, int type, const int*, int) const
    {
        writeElems(out, data, nelems, type, ' ');
    }
};

line:5 에서 상속받은 순수가상함수 write() 가 구현되어있다. 함수의 시그너처가 생략된 것은 이전글을 참고하자. 여기서는 별로 볼 것이 없다. 그냥 writeMat() 함수가 다시 호출된다.

static void writeMat(std::ostream& out, const Mat& m, char rowsep, char elembrace, bool singleLine)
{
    CV_Assert(m.dims <= 2);
    int type = m.type();

    char crowbrace = getCloseBrace(rowsep);
    char orowbrace = crowbrace ? rowsep : '\0';

    if( orowbrace || isspace(rowsep) )
        rowsep = '\0';

    for( int i = 0; i < m.rows; i++ )
    {
        if(orowbrace)
            out << orowbrace;
        if( m.data )
            writeElems(out, m.ptr(i), m.cols, type, elembrace);
        if(orowbrace)
            out << crowbrace << (i+1 < m.rows ? ", " : "");
        if(i+1 < m.rows)
        {
            if(rowsep)
                out << rowsep << (singleLine ? " " : "");
            if(!singleLine)
                out << "\n  ";
        }
    }
}

드디여 내가 생각하는 문제의 핵심에 다다렀다. 동시에 opencv 의 formatter 구조를 모두 살펴봤다.

line:3 을 보면 MatlabFormatter 에서 문제가 무엇인지 알 수 있다. Dimension 이 3 이상일 경우, 출력이 정상적으로 이뤄지지 않고 그냥 exception 을 던져버리도록 되어있다. 그러나 Matlab 의 경우에는 dimension 에 상관없이 결과값을 준다. 예를 들어, 아래의 코드에 대한 결과값 출력이 서로 다르게 나온다. 구조적으로 matlab formatter 와 default formatter 두 개로 나눠서 구현을 해야할 것 같다. 현재는 default formatter 를 호출하면 MatlabFormatter 가 적용되는데, 서로 format 이 다르기 때문이다. 실제 opencv 에 버그 리포팅 Broken MATLAB matrix output formatter (Bug #2789)을 보면,

Mat R = Mat(3, 3, CV_8UC2);
cv::randu(R, Scalar::all(0), Scalar::all(255));
cout << R << endl;      // Formatter::get("MATLAB")->write(cout, R);
Opencv default format:
[91, 2, 79, 179, 52, 205;
236, 8, 181, 239, 26, 248;
207, 218, 45, 183, 158, 101]
 
Matlab format:
(:,:,1) =
91 79 52
236 181 26
207 45 158
(:,:,2) =
2 179 205
8 239 248
218 183 101

Matlab 은 dimension 에 상관없이 이렇게 표시가 된다. 문제를 파악했으니 이제 구현 시작! 😉

Update
몇가지 잘못 생각한 것이 있어서 추가한다. opencv formatter 를 사용해서 N-dimensional Mat 를 출력하는 것이 크게 의미가 있는지에 대한 것이다. matlab formatter 뿐만 아니라 다른 formatter 에서도 공통적으로 사용이 되려면 2D 까지만 지원하는 것이 맞는것 같다. 굳이 N-dim 출력을 고집할 이유가 없다. 호환성을 생각한다면, 나머지 formatter 에서 지원을 못하면 의미가 없으니까. 특히 CSV 타입 같은 경우에는 표현할 수 있는 방법이 없다.

참고

Written by gomiski

2014/03/05 at 11:27 am

Missing Mat::zeros(int ndims, const int* sz, int type)

leave a comment »

어제 opencv 테스트 도중에 link error 가 나서 이상하다 싶은 함수가 있었다. 바로 Mat::zeros(int ndims, const int* sz, int type) 함수이다. N-dimension Mat 를 사용하기 위해서는 일반적인 2D + channel 방법을 사용하는 것이 아니라 ndim 과 sz 를 사용해서 Mat 를 만들어야 한다. 그러나 이를 0 으로 초기화하기 위한 함수가 구현이 되어있지 않은 것이다!! 보통은 opencv 에서 2D 이상은 잘 사용하지 않는 것이라 업데이트가 덜 된 것 같다. 혹시나 해서 찾아봤더니 opencv Q&A 커뮤니티에서도 동일한 질문이 있었고, 소스코드 업데이트가 있었는데 이유는 모르겠지만 다시 삭제가 되었다.

그러나~~ 어이없게도 github 의 master branch 에서는 이 구현이 그대로 살아있는 것이다. 왜 이럴까? 나도 모르겠다. 이참에 리플 달아서 포인트도 얻을 겸해서 답이 없었던 질문에 답을 달면서 도움을 요청했다.

이번엔 답을 달아주는 훌륭하신 분이 있을까?
기대가 된다.

와우~ 답을 달아주신 분이 있다!! ㅋㅋㅋ

Written by gomiski

2014/03/05 at 2:45 am

Posted in opencv, study

Tagged with , , , , , , , ,

TS – ts.hpp

leave a comment »

BaseTest 와  ts.hpp 를 거쳐서 다시 돌어온 부분이 바로 TS class 다. 이제 unit test 와 관련된 기본 구조는 이걸로 정리가 대충 다된 것 이라고 기대하며 같다. 이번에 살펴볼 클래스는 TS 다. BaseTest 안에 있으면서 test 와 관련된 가장 기본적인 작업을 하는 클래스다.

class CV_EXPORTS TS
{
public:
    TS();
    virtual ~TS();
    static TS* ptr();

    virtual void init( const string& modulename );                                               // initialize test system before running the first test
    virtual void update_context( BaseTest* test, int test_case_idx, bool update_ts_context );    // updates the context: current test, test case, rng state
    virtual void set_failed_test_info( int fail_code );                                          // sets information about a failed test
    virtual void printf( int streams, const char* fmt, ... );                                    // low-level printing functions that are used by individual tests and by the system itself
    virtual void vprintf( int streams, const char* fmt, va_list arglist );
    const TestInfo* get_current_test_info() { return &current_test_info; }
    virtual void set_gtest_status();

    enum { /* ... */ };
    enum { /* ... */ };    // test error codes

    CvFileStorage* get_file_storage();                     // get file storage
    RNG& get_rng() { return rng; }                         // get RNG to generate random input data for a test
    int get_err_code() { return current_test_info.code; }                          // returns the current error code
    double get_test_case_count_scale() { return params.test_case_count_scale; }    // returns the test extensivity scale
    const string& get_data_path() const { return data_path; }
    static string str_from_code( int code );               // returns textual description of failure code

protected:
    RNG rng;                       // these are allocated within a test to try keep them valid in case of stack corruption
    TestInfo current_test_info;    // information about the current test
    string data_path;              // the path to data files used by tests
    TSParams params;
    std::string output_buf[MAX_IDX];
};

Ahhhhhh~ 막상 다 까볼려고 했더니 이거 끝이 없구나. 대충 로직은 이해가 되는데, 하나하나 따라갈려니 원래 테스트코드 작성자가 무슨 용도로 만든 코드인지를 다 알아야 되는데 지금 나는 거기까지 알아낼 능력이 없구나. 위의 코드는 별다른 설명 없이도 주석으로 대강의 역할을 알 수 있다. 다만, get_current_test_info() 같은 함수가 실제로 어떤 상황에서 필요한 것인지는 테스트케이스를 생각하고 만들면서 알아내야겠다.

Written by gomiski

2014/03/03 at 7:42 am

Function Template

leave a comment »

기본적인 cpp 기능임에도 불구하고 여기에 다시 적는 것은, 너무 오래간만이라 까먹어서 다시 기록해 두는 것이다. 예제를 보는 것이 가장 확실한 사용법을 익히는 방법. 사용 예를 보면,

template<typename T>
void f(T s)
{
    std::cout << s << '\n';
}
 
void f<double>(double); // instantiates f<double>(double)
void f<>(char);         // instantiates f<char>(char)
void f(int);            // instantiates f<int>(int)

line:1~2 처럼 함수 선언 앞에 template 키워드를 넣어서 선언을 한다. 여기서 뒤에 나오는 typename 대신 class를 사용하는 것을 자주 볼 수 있다. 무슨 차이가 있을까? 당연히 세상은 혼자가 아니다. 궁금한 사람이 있고 답해주는 사람들도 많다.

Summary: Stroustrup originally used class to specify types in templates to avoid introducing a new keyword. Some in the committee worried that this overloading of the keyword led to confusion. Later, the committee introduced a new keyword typename to resolve syntactic ambiguity, and decided to let it also be used to specify template types to reduce confusion, but for backward compatibility, class kept its overloaded meaning.

According to Scott Myers, Effective C++ (3rd ed.) item 42 (which must, of course, be the ultimate answer) – the difference is “nothing”.

즉, 둘다 똑같으니 어느거나 써도 상관없다…랜다.

참고

Written by gomiski

2014/03/03 at 3:51 am

Posted in C++, General, Lecture

Tagged with , , , , ,