0

我正在编写一个使用模板化 operator<< 函数的日志记录类。我将模板函数专门用于宽字符串,以便在编写日志消息之前进行一些从宽到窄的翻译。我无法让 TCHAR 正常工作 - 它不使用专业化。想法?

这是相关的代码:

// Log.h header
class Log
{
  public:
    template <typename T> Log& operator<<( const T& x );

    template <typename T> Log& operator<<( const T* x );

    template <typename T> Log& operator<<( const T*& x );

    ... 
}

template <typename T> Log& Log::operator<<( const T& input )
{ printf("ref"); }

template <typename T> Log& Log::operator<<( const T* input )
{ printf("ptr"); }

template <> Log& Log::operator<<( const std::wstring& input );
template <> Log& Log::operator<<( const wchar_t* input );

和源文件

// Log.cpp 
template <> Log& Log::operator<<( const std::wstring& input )
{ printf("wstring ref"); }
template <> Log& Log::operator<<( const wchar_t* input )
{ printf("wchar_t ptr"); }
template <> Log& Log::operator<<( const TCHAR*& input )
{ printf("tchar ptr ref"); }

现在,我使用下面的测试程序来练习这些功能

// main.cpp - test program
int main()
{
 Log log;
 log << "test 1";
 log << L"test 2";
 std::string test3( "test3" );
 log << test3;
 std::wstring test4( L"test4" );
 log << test4;
 TCHAR* test5 = L"test5";
 log << test5;
}

运行上述测试显示以下内容:

// Test results
ptr
wchar_t ptr
ref
wstring ref
ref

不幸的是,这并不完全正确。我真的很希望最后一个是“TCHAR”,这样我就可以转换它。根据 Visual Studio 的调试器,当我进入测试 5 中调用的函数时,类型是 wchar_t*& - 但它没有调用适当的专业化。想法?

我不确定它是否相关,但这是在 Windows CE 5.0 设备上。

4

4 回答 4

5

TCHAR 是一个宏,它为 wchar_t 或 char 引用 typedef。当您实例化模板时,宏已经被替换。您最终将引用您的 char 模板实例或 wchar_t 模板实例。

于 2010-06-11T20:09:10.550 回答
1

(@jwismar 说的是正确的,但这不是代码的主要问题)。

考虑这段代码

void foo(const int*& p);

int main() {
  int *p = 0;
  foo(p); // ERROR: cannot initialize `const int *&` with `int *`
}

如果您尝试编译它,则会导致错误。错误的原因是不可能将类型的引用绑定到const int *&类型的指针int *。这将违反 const 正确性规则。

另一个更小的例子说明了同样的问题

int *p = 0;
const int *&rp = p; // ERROR: cannot initialize `const int *&` with `int *`

这实际上也是您在代码中遇到的问题。const TCHAR*&你用参数类型声明了你的模板

template <> Log& Log::operator<<( const TCHAR*& input )
{ printf("tchar ptr ref"); }

并期望它被称为TCHAR *类型的参数

TCHAR* test5 = L"test5";
log << test5;

是不可能的。您的模板不被视为调用的可行功能。要么添加const到参数类型,要么const从参数类型中删除。或者,也许,摆脱参考。(为什么将参数声明为对指针的引用?)

PS你可能会惊讶地发现编译器调用模板而不是你想要的目标函数

template <typename T> Log& Log::operator<<( const T& input )
{ printf("ref"); }

乍一看,编译器似乎在做我刚才所说的“不可能”。实际上,编译器正在做的是完全不同的事情。

后一个模板被实例化为T等于TCHAR *。怎么样,如果你仔细扩展上面的参数声明T == TCHAR *,你会得到TCHAR *const &,而不是const TCHAR *&。这是两个完全不同的东西。完全可以TCHAR *const &使用指针初始化引用TCHAR *,这正是编译器在您的情况下所做的。

回到我的简单例子

int *p = 0;
const int *&rp1 = p; // ERROR: cannot initialize `const int *&` with `int *`
int *const &rp2 = p; // OK, no error here
于 2010-06-11T20:47:56.627 回答
0

啊哈!

因此,我将问题分解为最小的部分,并发现了一些关于模板函数匹配顺序的信息。首先,我将程序分解为:

// main.cpp
int main()
{
 Log log;
 wchar_t* test = L"test";
 log << test;
 return 0;
}

和 // Log.h

class Log
{
  public:
   template <typename T> Log& operator<<( const T* x );
};

template <> Log& Log::operator<<( wchar_t* const & input );

和 // Log.ccp

#include "Log.h"

template <> Log& Log::operator<<( const wchar_t* input )
{ printf("wchar_t ptr\n"); return *this; }

果然,我的模板专业化被调用了。当我像这样向 Log 标头添加另一个专业化时

template <typename T> Log& operator<<( const T& x );

编译器开始匹配新函数。然而,这一次,我没有包含模板的定义,所以链接器抱怨了。链接器显示以下错误:

error LNK2019: unresolved external symbol "public: class Log & __thiscall Log::operator<<<wchar_t *>(wchar_t * const &)" (??$?6PA_W@Log@@QAEAAV0@ABQA_W@Z) referenced in function _main

这告诉我它试图匹配的类型:

wchar_t* const &

一个const指针,而不是一个指向 const 的引用!所以,现在我的程序可以工作了。我只是像这样专门化模板:

template <> Log& Log::operator<<( wchar_t* const & input );

感谢大家的帮助。

于 2010-06-11T21:06:32.557 回答
0

我认为缺乏 constness 会使编译器感到困惑。

尝试

        const TCHAR* test5 = L"test5";

甚至更正确

        const TCHAR* test5 = _T("test5");

我认为这会给你你所期望的。

于 2010-06-12T16:28:27.260 回答