C++ (partial) template specialization 질문입니다.

aeronova의 이미지

안녕하세요,
제가 template specialization을 하다가 막혀서 질문드립니다.
일단 기본 template 함수는 다음과 같구요,

template<typename T1, typename T2>
void printDebugMSG(const T1& msg1, const T2& msg2) {
    std::cout << msg1 << " : " << msg2 << std::endl;
}

이것을 다음과 같이 typedef로 정의한 Matrix type의 경우 다르게 처리하려고 합니다.
typedef std::vector<double> Row;
typedef std::vector<Row> Matrix;

첫번째 인자는 상관없고, 두번째 인자가 Matrix type인 경우를 하려고 하니 어떻게 해야 할 지 몰라서 일단 첫번째 인자도 const char*라고 두었습니다.
template<>
void printDebugMSG<const char* msg1, const Matrix& matrix>(const char* msg1, const Matrix& matrix) {
    std::cout << "TEMPLATE SPECIALIZATION" << std::endl;
}

이제 다음과 같은 예제를 이용하면
Matrix matrix;
printDebugMSG("Hello","World");
printDebugMSG("Hello",matrix);

... 무수한 해독 불가능한 에러들이 쭉~ 뜹니다.ㅜㅜ
debug.h:26: error: missing `>' to terminate the template argument list
debug.h:26: error: expected init-declarator before "const"
debug.h:26: error: expected `;' before "const"
debug.h: In function `void printDebugMSG(const T1&, const T2&) [with T1 = char[6
], T2 = Matrix]':
debugTest.cpp:8:   instantiated from here
debug.h:22: error: no match for 'operator<<' in 'std::operator<< [with _Traits =
 std::char_traits<char>](((std::basic_ostream<char, std::char_traits<char> >&)(+
std::operator<< [with _Traits = std::char_traits<char>](((std::basic_ostream<cha
r, std::char_traits<char> >&)(&std::cout)), ((const char*)msg1)))), ((const char
*)" : ")) << msg2'
d:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/ostre
am.tcc:63: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic
_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>&(*)(st
d::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_tr
aits<char>]
d:/Dev-Cpp/bin/../lib/gcc/mingw32/3.4.2/../../../../include/c++/3.4.2/bits/ostre
am.tcc:74: note:                 std::basic_ostream<_CharT, _Traits>& std::basic
_ostream<_CharT, _Traits>::operator<<(std::basic_ios<_CharT, _Traits>&(*)(std::b
asic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<cha
r>]
.....

저는 "no match for 'operator<<'"를 보고 혹시나 싶어서 template과 관련된 모든 <,>에 간격을 주어 보았는데, 별 소득이 없었습니다. :(
조언 부탁드립니다.
thyoo의 이미지

Before

template<>
void printDebugMSG<const char* msg1, const Matrix& matrix>(const char* msg1, const Matrix& matrix) {
    std::cout << "TEMPLATE SPECIALIZATION" << std::endl;
}

After
template<typename MSG>
void printDebugMSG(const MSG& msg, const Matrix& m)
{
    std::cout << "MATRIX" << std::endl;
}

debug이면 std::cout보다는 std::cerr를 쓰시지요.
저라며는

template<typename T1, typename T2>
struct debug_msg_ {
	const T1& msg1;
	const T2& msg2;
	debug_msg_(const T1& m1, const T2& m2) : msg1(m1), msg2(m2) {}
};
 
template<typename T1, typename T2> inline
debug_msg_<T1, T2> debug_msg(const T1& m1, const T2& m2)
{
	return debug_msg_<T1, T2>(m1, m2);
}
 
template<typename T1, typename T2> inline std::ostream&
operator<<(std::ostream& s, const debug_msg_<T1, T2>& dm)
{
	return s << dm.msg1 << " , " << dm.msg2 << std::endl;
}
 
template<typename T1> inline std::ostream&
operator<<(std::ostream& s, const debug_msg_<T1, int>& dm)
{
	return s << "int" << std::endl;
}
 
 
int main()
{
	std::cout << debug_msg(1, 10.0);
	std::cout << debug_msg(1, 1);
	return 0;
}

이쯤 해 주면, 다들 좋아합니다.
___________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

aeronova의 이미지

많은 것을 보여 주셔서 감사합니다. 덕분에 많은 것을 배웠습니다. :)
그런데,

1. 만일 인자 2개 다 특정 type을 가진 경우, template specialization을 어떻게 하는지 알고 싶습니다. 음... 예제 따라서 한다고 했는데, 제가 따라한 예제는 인자가 1개인 경우였거든요. 그래서 아마

template<>

이라고 사용된 듯 합니다. 저는 단순히 인자가 2개 모두 특정 type인 경우도 이와 동일하고 사용할 수 있다고 생각했는데, 생각처럼 잘 되질 않더군요.

2. 그리고, 보여주신 예제에 대한 설명 좀 부탁드립니다. struct을 public class로 사용해서 아예 debug 출력을 담당할 class를 만드신 것처럼 보입니다. 근데 인자 2개을 받는 constructor, 그러니까

template<typename T1, typename T2> inline
debug_msg_<T1, T2> debug_msg(const T1& m1, const T2& m2)
{
return debug_msg_<T1, T2>(m1, m2);
}

이 함수의 경우, debug_msg_ class에 함수형이 선언되지 않았는데, 이렇게 class 외부에 정의될 수 있는지요? "inline" 때문에 자동적으로 class안에서 처리되기 때문인가요?

그리고 생각해보니 debug 용 class는 function object의 형태로 구현해 주어도 될 듯 합니다. 이게 더 나은 방법일까요? 다시 조언 부탁드립니다.

It's better to burn out than to fade away. -- Kurt Cobain.

thyoo의 이미지

1.

template <typename T1, typename T2>
void foo(const T1& m1, const T2& m2)
{
	std::cout << m1 << " , " << m2 << std::endl;
}
 
void foo(const char* s, const int m)
{
	std::cout << s << ": int" << std::endl;
}
 
int main()
{
	foo("double", 10.0);
	foo("integer", 1);
	return 0;
}

2. debug_msg ctor아닙니다. 그냥 global function입니다.

- 의문 날 때 test program 짜서 돌려 보세요.
- 누가 친절하게 아리켜 주지 않기도 하고 10중 8,9는 스스로 답을 알 수 있읍니다.
- 하드웨어나 기타 공학 과학은 실험하는 데에 돈과 시간이 들지만 소프트웨어는 얼마나 쉽습니까?
__________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

aeronova의 이미지

void foo(const char* s, const int m)
{
std::cout << s << ": int" << std::endl;
}

보여주신 이 함수는 template specialization이 아니라 그냥 일반 함수이지 않나요?
제가 template 부분을 따로 헤더로 만들어 필요한 클래스 헤더에 include해서 쓰고 있어서,
이렇게 일반 함수로 처리하면 mutiple definition error가 날 듯 합니다.

그리고 debug_msg_ 를 public class로 생각한 것은 다음과 같은 constructor 부분때문이었습니다.

 debug_msg_(const T1& m1, const T2& m2) : msg1(m1), msg2(m2) {}

이런 건 class에서만 가능하지 않나요?

- 의문 날 때 test program 짜서 돌려 보세요. - 누가 친절하게 아리켜 주지 않기도 하고 10중 8,9는 스스로 답을 알 수 있읍니다. - 하드웨어나 기타 공학 과학은 실험하는 데에 돈과 시간이 들지만 소프트웨어는 얼마나 쉽습니까? 저는 생각하시는 것처럼 그냥 별 생각 없이 질문만 올리지 않습니다. 보여주신 일반 함수로 처리하는 방법도 이미 이것 저것 하다 template 형식을 포기하고 사용해 보았지만, 이 함수 정의 부분이 여러 class에 포함될 경우 에러를 만나서 template 형식을 유지하면서 할 방법을 알고 싶어서 재질문을 올렸던 것입니다. 제가 참고한 곳은 C++ FAQ의 template 부분(http://www.parashift.com/c++-faq-lite/templates.html#faq-35.7)으로 여기 보면 예제로 인자 하나인 함수의 template specialization을 보여주어서 따라했습니다. 근데 제 경우는 인자가 2개라서 단순히 흉내니 에러가 나더군요. :(

p.s.
그리고 추천해주신 std:cerr에 관해 찾아보니 std::clog가 있어서 이걸 쓰기로 했습니다.

It's better to burn out than to fade away. -- Kurt Cobain.

thyoo의 이미지

>> 이렇게 일반 함수로 처리하면 mutiple definition error가 날 듯 합니다.

분명 그 함수는 중복정의 에러납니다.

그런데 왜 헤더에 정의할 수 있는 게 템플릿만 있다고 생각하시죠?
inline만 붙이면 됩니다.

inline void foo(const char* s, const int m)
{
std::cout << s << ": int" << std::endl;
}

debug_msg_(const T1& m1, const T2& m2) : msg1(m1), msg2(m2) {}

얘는 ctor 맞습니다.

template<typename T1, typename T2> inline
debug_msg_<T1, T2> debug_msg(const T1& m1, const T2& m2)
{
return debug_msg_<T1, T2>(m1, m2);
}
얘는 글로벌 인라인 함수입니다.

여러 가지 테스트를 하셨다니
제 말이 억울하셨을 수도 있겠군요.
미안합니다.
하지만 debug_msg()에 대한 질문은
아직도 무슨 뜻인지 이해를 못하겠군요.
___________________________________
Less is More (Robert Browning)

___________________________________
Less is More (Robert Browning)

aeronova의 이미지

inline으로 처리하면 각각 호출된 class 안에 함수가 embed되어서 template 비슷하게 쓰일 수 있군요.
저는 inline이 단순히 performance 개선용으로 사용되는 줄 알았는데, 덕분에 다른 용도를 배웠습니다.

그리고 자세히 보니, debug_msg()를 struct으로 처리해서 단순히 member data만 초기화시키고, 이걸 template global 함수에서 부르면서 "operator <<"으로 다양한 type의 인자를 처리하는 방식이군요. 이런 방식에 익숙치 않아 선뜻 이해를 못했습니다. 예제 감사드립니다.

It's better to burn out than to fade away. -- Kurt Cobain.

doldori의 이미지

thyoo님이 다 설명해 주셨으니 저는 왜 처음 코드가 잘못인지만 설명드리죠.

template<typename T1, typename T2>
void printDebugMSG(const T1& msg1, const T2& msg2);  // (1)
 
template<>
void printDebugMSG<char*, Matrix>(const char* msg1, const Matrix& matrix);  // (2)

(2)는 (1)의 specialization이 될 수 없습니다. (1)의 첫번째 인자는 참조형인데 (2)는 그렇지 않기 때문입니다.
그럼 (2)를 참조형으로 고치면 되겠는데, 그렇게 하면 다른 문제가 발생합니다.
만약 (2)를 호출하는 코드가
printDebugMSG("Hello",matrix);

라면 첫번째 인자의 형은 const char[6]이므로
template<>
void printDebugMSG<char[6], Matrix>(const char (&msg1)[6], const Matrix& matrix);  // (2')

로 하면 됩니다. 이제 문제는 "Hello"가 아닌 "Hi" 등으로 호출하면 첫번째 인자의 형이 달라지기 때문에
(2')가 아니라 (1)이 호출된다는 것입니다. 범용성이 없어지는 거죠. 이것은 참조형 인자를 받는 함수 템플릿을
string literal로 호출할 때 헷갈리기 쉬운 문제입니다.

또다른 예로

template<typename T>
void f(const T& arg1, const T& arg2);
 
f("Hello", "Hi");  // ambiguous: const char[6] or const char[3] for T?

aeronova의 이미지

답변 감사합니다. 넘겨주는 인자의 type이 pointer인지 reference인지에 따라 다르게 인식되는 문제가 핵심이었군요. 주의해야겠습니다.

It's better to burn out than to fade away. -- Kurt Cobain.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.