Специализация шаблонных функций C ++ с использованием TCHAR в Visual Studio 2005

Я пишу класс ведения журнала, в котором используется шаблонная функция оператора ‹<. Я специализируюсь на функции шаблона для строки с широкими символами, чтобы я мог сделать некоторый перевод от широкого к узкому перед написанием сообщения журнала. Я не могу заставить 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.


person Eli    schedule 11.06.2010    source источник


Ответы (4)


TCHAR - это макрос, который ссылается на typedef для wchar_t или для char. К моменту создания экземпляров шаблонов макрос уже был заменен. В конечном итоге вы будете ссылаться либо на свой экземпляр шаблона для char, либо на экземпляр для wchar_t.

person jwismar    schedule 11.06.2010
comment
Спасибо, но я знаю, какая функция вызывается. Что я хотел бы знать, так это то, как я могу специализировать свой шаблон для TCHAR *, поскольку сейчас он не вызывает другие мои специализации. - person Eli; 12.06.2010
comment
Не думаю, что вы понимаете ... TCHAR не типаж. Шаблоны не знают, что такое TCHAR, когда препроцессор готов. Все отображаемые шаблоны - это либо char, либо wchar_t. - person Cogwheel; 12.06.2010
comment
достаточно честно - спасибо. Замените все TCHAR на wchar_t. Результаты остались прежними. Другими словами, если я изменю test5 с TCHAR на wchar_t, он не вызовет мою специализацию wchar_t. - person Eli; 12.06.2010
comment
@Eli: См. Мой ответ для объяснений. - person AnT; 12.06.2010

(То, что говорит @jwismar, правильно, но это не основная проблема с кодом).

Рассмотрим этот фрагмент кода

void foo(const int*& p);

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

Если вы попытаетесь его скомпилировать, это приведет к ошибке. Причина ошибки в том, что невозможно привязать ссылку типа const int *& к указателю типа int *. Это нарушит правила константной корректности.

Другой, меньший пример, иллюстрирующий ту же проблему.

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 из типа параметра. Или, может быть, избавиться от ссылки. (Почему вы объявляете параметр как ссылку на указатель?)

P.S. Может показаться удивительным, что вместо желаемой целевой функции компилятор вызывает шаблон

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
person AnT    schedule 11.06.2010
comment
Спасибо за подробное объяснение. Я выбрал константную ссылку, потому что решил, что компилятор обнаружит wchar_t * как указатель, найдет соответствующую функцию, возможно, найдет версию с константной ссылкой и использует ее. я хотел попытаться предотвратить это. - person Eli; 12.06.2010
comment
Подождите, вы не совсем правы. Test5 соответствует функции, для которой требуется параметр const. Итак, я не нарушаю константную корректность, потому что это работает. Моя проблема связана с порядком приоритета для оценки того, какая перегруженная функция шаблона вызывается. - person Eli; 12.06.2010
comment
@Eli: Нет, я сказал абсолютно правильно. Test5 действительно соответствует шаблонной функции, но выводит T как TCHAR *. Теперь const T * для T = TCHAR * эквивалентно TCHAR* const&. Ваша желаемая функция объявлена ​​как const TCHAR*&, что совсем другое дело. Опять же: можно привязать ссылку TCHAR * const & к указателю TCHAR *, но невозможно привязать ссылку const TCHAR *& к указателю TCHAR *. - person AnT; 12.06.2010
comment
@Eli, TCHAR * будет соответствовать const TCHAR *, но не const TCHAR *&. Он также будет соответствовать const wchar_t *, потому что TCHAR и wchar_t - это одно и то же. - person Mark Ransom; 12.06.2010
comment
Спасибо, ребята, я разобрался, как только вы их выкладывали :) - person Eli; 12.06.2010

A-HA!

Итак, я разбил проблему на самые мелкие части и обнаружил кое-что о порядке, в котором сопоставляются функции шаблона. Сначала я разбил программу на следующее:

// 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; }

И, конечно же, моя специализация по шаблонам была названа. Когда я добавил еще одну специализацию в заголовок журнала, как это

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, а не ссылка на указатель на константу! Итак, теперь моя программа работает. Я просто специализирую шаблон так:

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

Спасибо всем за помощь.

person Eli    schedule 11.06.2010

Я думаю, что компилятор сбивает с толку отсутствие константности.

Пытаться

        const TCHAR* test5 = L"test5";

или даже правильнее

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

Я думаю, это даст вам то, чего вы ожидаете.

person Michael J    schedule 12.06.2010