【问题标题】:C++ template function specialization using TCHAR on Visual Studio 2005在 Visual Studio 2005 上使用 TCHAR 的 C++ 模板函数特化
【发布时间】:2011-03-02 20:04:08
【问题描述】:

我正在编写一个使用模板化运算符

以下是相关代码:

// 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 设备上。

【问题讨论】:

    标签: c++ templates template-specialization wchar-t widestring


    【解决方案1】:

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

    【讨论】:

    • 谢谢,但我知道调用了哪个函数。我想知道如何将我的模板专门用于 TCHAR*,因为现在它不调用我的其他专业。
    • 我想你不明白... TCHAR 不是一种类型。一旦预处理器完成,模板就不知道 TCHAR 是什么。看到的所有模板都是 char 或 wchar_t。
    • 很公平 - 谢谢。将所有 TCHAR 替换为 wchar_t。结果还是一样。换句话说,如果我将 test5 从 TCHAR 更改为 wchar_t,它不会调用我的 wchar_t 特化。
    • @Eli:请参阅我的答案以获得解释。
    【解决方案2】:

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

    考虑这段代码

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

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

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

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

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

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

    并期望它被 TCHAR * 类型的参数调用

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

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

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

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

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

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

    回到我的简单例子

    int *p = 0;
    const int *&rp1 = p; // ERROR: cannot initialize `const int *&` with `int *`
    int *const &rp2 = p; // OK, no error here
    

    【讨论】:

    • 感谢您的深入解释。我选择了 const 引用,因为我认为编译器会将 wchar_t* 检测为指针,寻找匹配的函数,可能会找到 const 引用版本,然后使用它。我想尽量防止这种情况发生。
    • 等一下,你还没有完全正确。 Test5 匹配需要 const 参数的函数。所以,我没有破坏 const 正确性,因为它确实有效。我的问题与评估调用哪个重载模板函数的优先顺序有关。
    • @Eli:不,我说的完全正确。 Test5 确实与模板函数匹配,但它将T 派生为TCHAR *。现在,const T * for T = TCHAR * 等价于 TCHAR* const&amp;。您想要的函数被声明为const TCHAR*&amp;,这是完全不同的事情。同样:可以将TCHAR * const &amp; 引用绑定到TCHAR * 指针,但不能将const TCHAR *&amp; 引用绑定到TCHAR * 指针。
    • @Eli,TCHAR * 将匹配 const TCHAR *,但不匹配 const TCHAR *&amp;。它也将匹配 const wchar_t *,因为 TCHAR 和 wchar_t 是同一个东西。
    • 谢谢大家,我在您发布这些内容时已经整理好了 :)
    【解决方案3】:

    啊哈!

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

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

    并且 // 日志.h

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

    并且 // 日志.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 );
    

    感谢大家的帮助。

    【讨论】:

      【解决方案4】:

      我认为缺少 const 会使编译器感到困惑。

      试试

              const TCHAR* test5 = L"test5";
      

      甚至更准确

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-01
        • 1970-01-01
        • 2018-10-07
        • 2011-06-27
        • 1970-01-01
        相关资源
        最近更新 更多