C++/CLI 템플릿 클래스에서 연산자 오버로딩 문제

  • Post author:
  • Post category:
  • Post comments:8 Comments
  • Post last modified:February 7, 2020

아무래도 컴파일러 버그인 듯한 문제를 발견했는데, 단순히 내 자신이 무지한 탓인가 싶어 블로그에 올려봅니다. 소스 코드는 Visual Studio 2008로 작성해서 올려놨습니다.

// LibraryA.cpp : library file.
#include "stdafx.h"
#include <iostream>

using namespace System;

template<typename ABC>
public ref class ManagedClassA
{
public:
	int V;

	ManagedClassA(int v)
	{
		V = v;
	}

	static bool operator == (ManagedClassA^ a, ManagedClassA^ b)
	{
		// Return true if the fields match:
		return ( (a->V + b->V) % 2 == 0 );
	}

	static bool operator != (ManagedClassA^ a, ManagedClassA^ b)
	{
		return !(a == b);
	}
};

public ref class ManagedClassB : public ManagedClassA<int>
{
public:
	ManagedClassB(int v)
		: ManagedClassA(v)
	{
	}
};

// 단순 테스트용 클래스
public ref class Tester
{
public:
	static void Test1()
	{
		ManagedClassB^ b1 = gcnew ManagedClassB(1);
		ManagedClassB^ b2 = gcnew ManagedClassB(3);
		if(b1 == b2)
			System::Console::WriteLine("성공");
		else
			System::Console::WriteLine("실패");
	}
};


template<typename ABC>
public ref class ManagedClassC
{
public:
	int V;

	ManagedClassC(int v)
	{
		V = v;
	}

	static bool operator == (ManagedClassC^ a, ManagedClassC^ b)
	{
		// Return true if the fields match:
		return ( (a->V + b->V) % 2 == 0 );
	}

	static bool operator != (ManagedClassC^ a, ManagedClassC^ b)
	{
		return !(a == b);
	}
};

public ref class ManagedClassD : public ManagedClassC<int>
{
public:
	ManagedClassD(int v)
		: ManagedClassC(v)
	{
	}
};
// LibraryB.cpp : main project file.
#include "stdafx.h"

using namespace System;

void Test2()
{
	ManagedClassD^ b1 = gcnew ManagedClassD(1);
	ManagedClassD^ b2 = gcnew ManagedClassD(3);
	if(b1 == b2)
		System::Console::WriteLine("성공");
	else
		System::Console::WriteLine("실패");
}

int main(array<System::String ^> ^args)
{
	Tester::Test1();
	Test2();
    return 0;
}

LibraryA는 dll로 빌드한 닷넷 어셈블리고, LibraryB는 콘솔 애플리케이션입니다. 여기서 주목할 것은 LibraryA의 클래스 A, B와 클래스 C, D입니다. 두 코드는 동일합니다. 클래스 A와 클래스 C, 그리고 클래스 B와 클래스 C는 완전히 똑같습니다. 글쎄요. 차이점이 뭘까요? 단 하나입니다. 클래스 A와 B의 연산자 오버로딩을 테스트하는 코드는 LibraryA에 있지만, 클래스 C와 B를 테스트하는 코드는 LibraryB에 있습니다. 얼핏 생각하기엔 완전히 같은 코드지만 실제로 돌려보면 그렇지가 않습니다. 과연 어떤 결과가 나올까요?

성공
실패

테스트 코드가 LibraryA에 있으면 성공하지만 그렇지 않으면 실패합니다. 이게 도대체 어떻게 된 일일까요? 컴파일된 코드를 까보면 문제가 확실해집니다. Reflector를 이용해 컴파일된 코드를 열어봅니다.

C++/CLI 템플릿 클래스에서 연산자 오버로딩 문제

ClassA엔 연산자 오버로딩한 메서드 op_Equality가 있지만, ClassB엔 없습니다.

자, 이게 도대체 무슨 영문일까요? 컴파일러가 바보인가요? 아니면 제가 당연한 걸 모르는 건가요?

PS. C++/CLI에서의 연산자 오버로딩 패턴에 해결책을 적었습니다.

Author Details
Kubernetes, DevSecOps, AWS, 클라우드 보안, 클라우드 비용관리, SaaS 의 활용과 내재화 등 소프트웨어 개발 전반에 도움이 필요하다면 도움을 요청하세요. 지인이라면 가볍게 도와드리겠습니다. 전문적인 도움이 필요하다면 저의 현업에 방해가 되지 않는 선에서 협의가능합니다.
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

8 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
시즈하
16 years ago

일단 제가 테스트해본 결과로는, LibraryA내에서 operator==이 호출된 적이 없으면 실패하네요.

Class D에서 별 의미도 없이 operator==를 한번 호시켜 봤더니 성공으로 나왔습니다.

최적화 옵션이 꺼져있는데도 operator==에 대한 호출이 없으면 링크에서 빼버리는 것으로 생각됩니다.

시즈하
16 years ago

그리고 아마도 operator==에 대한 정의가 template쪽에 있을 경우에 그런 문제가 있는 듯 합니다.

최재훈
16 years ago

네, 제가 말하는 문제가 바로 그겁니다. 아무리 생각해도 정상적인 행동이 아니고, 컴파일러 문제인 듯 한데 참 곤란하네요.

dlarudugs20
dlarudugs20
13 years ago

C++/CLI에서 template 키워드를 쓰면 Native C++과 같이 컴파일 타임 코드 생성을 합니다.
C#에서처럼 쓰실려면 template 키워드 대신 generic키워드를 쓰시면 됩니다.
저도 이것땜에 고생 엄청 했어여ㅠㅠㅠ

CHOI, Jaehoon
13 years ago
Reply to  dlarudugs20

generic은 제약이 많아서 여기선 일부러 template 쓴 겁니다. C++/CLI에서 생성한 template 클래스는 C#에 가져가 쓰지 못하지만, C++/CLI 코드 내에서는 강력하기 때문에 자주 사용합니다. generic 이 아닌 template 이라 해서 연산자 오버로딩을 컴파일 안 하는 것도 아니구요.

Dlarudugs20
Dlarudugs20
13 years ago
Reply to  CHOI, Jaehoon

하지만 말했듯이 template키워드를 쓰면 컴파일 타임 코드 생성을 하기 때문에 빌드하는 cpp파일에서 호출하지 않거나 구체화를 하지 않으면 코드 자체가 생성이 않됩니다.

CHOI, Jaehoon
13 years ago
Reply to  Dlarudugs20

이 문제는 예전에 해결한 기억이 나는데 역시나 찾아보니 해결법을 적어놨네요. https://andromedarabbit.net/cpp_cli_operator_overloading_patterns/

Dlarudugs20
Dlarudugs20
13 years ago
Reply to  CHOI, Jaehoon

아, virtual 키워드를 붙이면 함수의 본문이 있어야 하니까 만드는군요.
좋은 정보 감사합니다.