반응형

신기한 C++문법들을 탐방하던 도중 이런 주제를 목격했다.
그 주제는 런타임중 해당 인스턴스의 멤버변수가 존재하는지 알 수 있는가? 에 대한 주제였다.
ㄹㅇ 개궁금하지 아니한가? 플머라면 못 참는 주제다.. 바로 들어가봤다.
c++ - 클래스에 특정 멤버 변수가 있는지 감지하는 방법은 무엇입니까? - 스택 오버플로 (stackoverflow.com)

How to detect whether there is a specific member variable in class?

For creating algorithm template function I need to know whether x or X (and y or Y) in class that is template argument. It may by useful when using my function for MFC CPoint class or GDI+ PointF c...

stackoverflow.com

외국 형님들이 엄청나게 길게 설명해주셨다.
 
실제로 코드를 복사해서 붙여넣으니까 동작하는거임 ㅁㅊ 대체 왜 되는거임?
아래 링크가 외국 성님들이 짜주신 코드다...

#include <type_traits>

template<typename T, typename = void>
struct has_id : std::false_type { };

template<typename T>
struct has_id<T, decltype(std::declval<T>().id, void())> : std::true_type { };

#include <iostream>

struct X { int id; };
struct Y { int foo; };

int main()
{
    std::cout << std::boolalpha;
    std::cout << has_id<X>::value << std::endl;
    std::cout << has_id<Y>::value << std::endl;
}

이거 때문에 해보지도 않은 메타프로그래밍을 처음 접하게 되었다....
이 코드를 해석하면서 배운 중요한 개념이 있는데,
첫 번째로, decltype, std::declval
두 번째로, SFINAE(Substitution Failure Is Not An Error) 기법
세 번째로, 원래 좀 알고 있긴 했지만 애매하게 알고있던 템플릿 특수화
네 번째로, 콤마 연산자(중요한건 모르겠고 이런걸 처음 봄)
이렇게 4개정도 되는듯? 
이 4개를 여기에 다 설명하면 블로그 글을 500만줄을 써야하므로 생략하겠습니다.. 보고오시길 ㅠ


제 누추한 실력으로 알아본 간략한 원리 및 흐름을 위 코드를 통해 설명해 보자면...
has_id라는 기본 템플릿 구조체특수화 템플릿 구조체, 즉 두개를 만들어준다.
decltype(std::declval<T>().id, void()) 을 통해 멤버변수가 있는지 판별한다. 원리는 다음과 같다.
 
id가 있는 경우 : declval가 타입추론에 성공하면서, 콤마 연산자를 통해 추론값을 버리고 decltype은 void 자료형을 추론하게 된다.(여기서 declval을 쓰는 이유가 있는데, 이 글을 참고하면 좋다.) 자료형 추론에 성공하며 템플릿 두번 째 인자가 정상적으로 대입되어 특수화 템플릿 구조체가 실행된다. 이 경우 std::true_type을 상속받으므로 value값은 true가 된다.
 
id가 없는 경우 : declval이 타입추론에 실패하면서 컴파일 에러가 나야하지만, 이 경우에 신기하게도 특수화 템플릿을 무시하고 유효한 인자가 있는 기본 템플릿이 사용된다.(이 기법이 SFINAE기법이라고 하네요?) 이 경우 std::false_type을 상속받으므로 value값은 false가 된다.
 
위 코드를 보고 공부한 것을 토대로 내 입맛대로 변경해봤다.
람다는 솔직히 쓸 일 없을 줄 알고 공부안했는데, 이럴 때 쓰더라.....
그래서 GPT쌤의 도움을 좀 받았는데, 더 공부해야 될 것 같다.....
 

#include <iostream>
#include <type_traits>

// 매크로로 멤버 존재 여부를 체크하는 유틸리티 매크로화
#define CREATE_MEMBER_CHECK(member)                                       \
namespace CheckMember {                                                   \
template<typename T, typename = void>                                     \
struct has_##member : std::false_type {};                                 \
                                                                          \
template<typename T>                                                      \
struct has_##member<T, decltype(std::declval<T>().member, void())>        \
    : std::true_type {};                                                  \
}                                                                         \
// 객체와 멤버 이름을 받아서 멤버 존재 여부를 검사하는 람다
#define HAS_MEMBER(instance, member)                                           \
    [] (const auto& obj) -> bool {                                             \
        bool has_member = CheckMember::has_##member<decltype(obj)>::value;     \
        return has_member;                                                     \
    } (instance)                                                               \

// idX, idY 멤버를 가진 구조체 생성
struct X { int idX; };
struct Y { int idY; };
struct XY { int idX; int idY; };

// idX, idY 멤버가 있는지 검사하는 구조체 생성
CREATE_MEMBER_CHECK(idX)
CREATE_MEMBER_CHECK(idY)
int main()
{
    X x;
    Y y;
    XY xy;
    std::cout << std::boolalpha; // bool을 true, false로 출력시키는 기능
    std::cout << "x has idX : " << HAS_MEMBER(x, idX) << std::endl;
    std::cout << "x has idY : " << HAS_MEMBER(x, idY) << std::endl;
    std::cout << "y has idX : " << HAS_MEMBER(y, idX) << std::endl;
    std::cout << "y has idY : " << HAS_MEMBER(y, idY) << std::endl;
    std::cout << "xy has idX : " << HAS_MEMBER(xy, idX) << std::endl;
    std::cout << "xy has idY : " << HAS_MEMBER(xy, idY) << std::endl;
}
실행 결과

오늘은 제가 메타프로그래밍이란 것을 처음 접해봤는데요?
사실 템플릿을 이용한 흑마법이라고 밖에 생각이 안듬 ㅋㅋ 그냥 광기의 영역 ㅇㅇ.....
진짜 공부하면서 알아갈때마다 충격의 연속이었읍니다. 처음보는 문법에... 이걸 이렇게 쓸 생각을 한다는거에...
근데 사실 이걸 쓸 일이 있을까? 싶다요?
여러분들도 미치지 말고 오래 삽시다!

반응형
반응형

생성자 상속이라는 문법이 있다.

https://learn.microsoft.com/ko-kr/cpp/cpp/constructors-cpp?view=msvc-170#inheriting_constructors

 

class Base {
public:
	Base()
	{
		std::cout << "Call : Base()" << '\n';
	}
	Base(int a)
	{
		std::cout << "Call : Base(int)" << '\n';
	}
	Base(const std::string& a)
	{
		std::cout << "Call : Base(const std::string&)" << '\n';
	}
};

class Derived : public Base {
public:
	using Base::Base;
};

int main()
{
	Derived d1;
	Derived d2(0);
	Derived d3("0");
	return 0;
}

원래 C++은 클래스를 상속할 경우 부모생성자의 호출을 위해 자식클래스의 생성자를 명시적으로 적을 필요가 있었다.

하지만 생성자 상속이란 문법을 사용해서 생략할 수 있다 (ㄷㄷ)

위 코드에서 Derived클래스는 원래 같았으면 부모생성자의 생성자를 명시적으로 적어줘야 했지만 using Base::Base;를 써줌으로써 기본 생성자, int형을 받는 생성자, const std::string&를 받는 생성자가 자동으로 정의된다.

 

-실행결과

 

잘쓰면 코드가독성에 매우 도움이 될 것 같다? 

반응형
반응형
#include <iostream>
#include <assert.h>

using namespace std;

class String
{
private:
	char* str;
	int lenght;
	int capacity;
public:
	String() : lenght(0), capacity(0) {}
	String(const char* _str)
		: lenght(strlen(_str)),
		capacity(lenght * 2 + 1)
	{
		str = new char[capacity];
		strcpy_s(str, lenght + 1, _str);
	}
	String(const String& _str)
		: lenght(strlen(_str.str)),
		capacity(lenght * 2  + 1)
	{
		str = new char[capacity];
		strcpy_s(str, lenght + 1, _str.str);
	}

	~String()
	{
		delete[] str;
	}
	
	int Size() { return lenght;  }
	int Capacity() { return capacity; }

	bool operator==(const char* _dest)
	{
		return (strcmp(this->str, _dest) == 0);
	}
	bool operator==(const String& _dest)
	{
		return (strcmp(this->str, _dest.str) == 0);
	}

	void operator=(const char* _str)
	{
		int len = strlen(_str);
		if (len + 1 >= capacity)
		{
			delete[] str;
			capacity = len * 2;
			str = new char[capacity];
		}
		strcpy_s(str, len + 1, _str);
		lenght = len;
	}
	void operator=(const String& _str)
	{
		int len = _str.lenght;
		if (len + 1 >= capacity)
		{
			delete[] str;
			capacity = len * 2;
			str = new char[capacity];
		}
		strcpy_s(str, len + 1, _str.str);
		lenght = len;
	}

	String& operator+=(const char* _str)
	{
		int len = strlen(_str);
		if (lenght + len + 1 > capacity)
		{
			capacity = (lenght + len) * 2 + 1;
			char* tempStr = new char[capacity];
			strcpy_s(tempStr, capacity, str);
			delete[] str;  // 기존 메모리 해제
			str = tempStr; // 새로운 메모리로 교체
		}
		strcat_s(str, capacity, _str); // str에 _str 추가
		lenght += len;
		return *this;
	}

	String& operator+=(const String& _str)
	{
		int len = _str.lenght;
		if (lenght + len + 1 > capacity)
		{
			capacity = (lenght + len) * 2 + 1;
			char* tempStr = new char[capacity];
			strcpy_s(tempStr, capacity, str);
			delete[] str;  // 기존 메모리 해제
			str = tempStr; // 새로운 메모리로 교체
		}
		strcat_s(str, capacity, _str.str); // str에 _str 추가
		lenght += len;
		return *this;
	}

	String operator+(const char* _str)
	{
		char* string = new char[lenght + strlen(_str) + 1];
		strcpy_s(string, lenght + 1, str);
		strcat_s(string, lenght + strlen(_str) + 1, _str);
		String temp = string;
		delete[] string;
		return temp;
	}
	String operator+(String& _str)
	{
		char* string = new char[lenght + _str.lenght + 1];
		strcpy_s(string, lenght + 1, str);
		strcat_s(string, lenght + strlen(_str.str) + 1, _str.str);
		String temp = string;
		delete[] string;
		return temp;
	}

	char& operator[](int _index)
	{
		if (_index > lenght) assert(false && "IndexError");
		return str[_index];
	}

	friend ostream& operator <<(ostream& _outStream, const String& _str);
};

ostream& operator <<(ostream& _outStream, const String& _str) {
	cout << _str.str;
	return _outStream;
}

int main()
{
	String s1 = "abc";
	cout << "s1 : " << s1 << '\n';

	String s2 = "def";
	cout << "s2 : " << s2 << '\n';

	String s3 = s1 + s2;
	cout << "s3 : " << s3 << '\n';

	String s4 = "";
	s4 += s3;
	cout << "s4 : " << s4 << '\n';

	String s5;
	s5 = s4 + "qwe";
	cout << "s5 : " << s5 << '\n' << '\n';

	cout << "s1 == s2 : " << (s1 == s2) << '\n';
	cout << "s3 == s4 : " << (s3 == s4) << '\n';
	cout << "s1 == abc : " << (s1 == "abc") << '\n';

	cout << "s1[0] ~ s1[3] : ";
	for (int i = 0; i < s1.Size(); i++)
	{
		cout << s1[i];
		if (i != s1.Size() - 1) cout << ", ";
	}

	return 0;
}

 

과제하는겸 string클래스를 구현해보았다.

메모리 할당 delete하는데 에러가 떠서 좀 고생함

반응형
반응형

오늘 수업때 인접리스트로 DFS구현을 해보라 하셨는데 뭔가 생각한대로 잘되서 기분이가 좋았음

#include <iostream>
#include <vector>

using namespace std;

void 재귀(int start, const vector<vector<int>>& edges, vector<bool>& visited)
{
    if (visited[start]) return;
    visited[start] = true;
    cout << start << ", ";
    for (int i = 0; i < edges[start].size(); i++)
    {
        재귀(edges[start][i], edges, visited);
    }
}

void DFS(int start, const vector<vector<int>>& edges)
{
    vector<bool> visited(edges.size(), false);
    재귀(start, edges, visited);
}

int main()
{
    vector<vector<int>> edges;
    edges.push_back({ 1, 2 });      //0
    edges.push_back({ 3, 6, 7 });   //1
    edges.push_back({ 4, 5 });      //2
    edges.push_back({ 6, 7 });      //3
    edges.push_back({ 8, 9 });      //4 
    edges.push_back({ 10, 11 });    //5
    edges.push_back({ });           //6
    edges.push_back({ });           //7
    edges.push_back({ });           //8
    edges.push_back({ });           //9
    edges.push_back({ });           //10
    edges.push_back({ });           //11

    cout << "DFS: ";
    DFS(0, edges);
    cout << endl;

    return 0;
}

 

위와 같은 그래프가 있다 하고 인접리스트를 작성했다 ㅋㅋ;

 

근데 실수로 무향그래프인데 단방향그래프라 생각하고 짜버림 그리고 너무 쉽게 생겨먹음

그래서 좀 이상한 무방향그래프를 가져와봄

이걸 기반으로 해보자

 

된다 ㅋㅋ 기모띠 기분 굿 나이스입니다 좋은 하루 되세요~

반응형

+ Recent posts