2013-07-11 4 views
1

Я создал шаблон функции определяется какНеоднозначность параметр шаблона

template < typename _Iter8, typename _Iter32 > 
int Utf8toUtf32 (const _Iter8 & _from, const _Iter8 & _from_end, _Iter32 & _dest, const _Iter32 & _dest_end); 

Отредактировано: первый параметр должен быть константным типа.

Первый и третий параметры изменяются, чтобы отражать их новое положение. Второй и четвертый параметры отмечают верхнюю границу итерации.

Я надеюсь реализовать логику «одна функция подходит всем». Единственное условие состоит в том, что оба типа _Iter имеют один и тот же тип и являются разыскаемыми. Также я хочу, чтобы параметры шаблона были выведены.

Первая проблема, которую я столкнулся, было

char utf8String [] "...some utf8 string ..."; 
wchar_t wideString [ 100 ]; 
char * pIter = utfString; 
Utf8toUtf16(pIter, pIter + n, wideString, wideString + 100); 

_Iter16 неоднозначно. Я предполагаю, потому что компилятор видит третий параметр как wchar_t[ 100 ] type, а четвертый - как wchar_t* type. Поправьте меня если я ошибаюсь. Изменение кода на:

Utf8toUtf16(pIter, pIter + n, (wchar_t*)wideString, wideString + 100); 

Устраняет проблему. Уродливый, но работает.

Тогда я ударил еще одну проблему:

unsigned long nCodepoint; 
Utf8toUtf32(pIter, pIter + n, &nCodepoint, &nCodepoint + 1)); 

Очевидно, что если бы я изменил nCodepoint быть типом массива и применил тот же бросок, как и первый, он будет компилировать.

Я не уверен, что я неправильно определил параметры шаблона. Мой вопрос: как я правильно кодирую это, учитывая мои ограничения выше, и есть ли способ сделать это, не прибегая к кастам?

Редактировать: Как указано в Jogojapan и DyP, приведенный выше состав не должен компилироваться. Я должен был бы вместо этого создать новый указатель на фронт массива и передать его. Что касается nCodepoint, возможно, мне придется создать его как массив длиной 1.

+2

В стороне: имена, такие как '_Iter8' и' _Iter32' (ведущее подчеркивание, за которым следует символ подчеркивания или заглавная буква) зарезервированы, поэтому ваш код имеет неопределенное поведение. –

+0

C++ 11? Используйте ['std :: begin'] (http://en.cppreference.com/w/cpp/iterator/begin) и [' std :: end'] (http://en.cppreference.com/w/ cpp/iterator/end) (даже для массивов). Не C++ 11? Напишите свой собственный. – dyp

+2

Вторая проблема связана с тем, что вы передаете свои итераторы в качестве ссылок. Есть ли причина, почему вы это делаете? Передача итераторов копией должна быть прекрасной. – jogojapan

ответ

3

Как jogojapan действительно дал ответ, я сделаю это сообщество wiki.

ИМО, это адекватное решение:

template < typename Iter8, typename Iter32 > 
Iter32 Utf8toUtf32(Iter8 _from, Iter8 _from_end, Iter32 _dest, Iter32 _dest_end); 

Это предназначено, чтобы вернуть то, что вы хотели _dest, чтобы изменить.

Если вам действительно нужно вернуть int, вы можете вернуть пару.

Чтобы отразить, какие итераторы должны быть прочитаны и которые должны быть записаны, вы можете использовать схему именования для параметров шаблона, например. InputIterator8 и OutputIterator32.


Чтобы дать аналогию с функцией стандартной библиотеки:

std::vector<int> v = {1,2,3,4}; 
for(auto i = v.begin(); i != v.end();) 
{ 
    if(*i == 2) 
    { 
     i = v.erase(i); // iterator invalidated and new "next" iterator returned 
    } 
} 

Если вы хотите, чтобы ваша функция а) принимать массивы и б) быть аналогичны функциям стандартной библиотеки, я не» Нет другого пути, кроме как вернуть «измененные» итераторы.Единственная функция библиотеки, которую я знаю, которая фактически меняет пройденный итератор, равна std::advance.

Пример:

template < typename Iter8, typename Iter32 > 
std::tuple<int, Iter8, Iter32> Utf8toUtf32(Iter8 _from, Iter8 _from_end, 
              Iter32 _dest, Iter32 _dest_end); 

char utf8String [] = "...some utf8 string ..."; 
wchar_t wideString [ 100 ]; 
char* pUtf8Res = nullptr; 
wchar_t* pUtf16Res = nullptr; 
int res = 0; 
std::tie(res, pUtf8Res, pUtf16Res) = Utf8toUtf16(begin(pIter), end(pIter), 
             begin(wideString), end(wideString)); 

(Редактируйте jogojapan)

Если вы должны держать прохождения итераторы как ссылки, потому что вы хотите, чтобы обновить положение текста они указывают на обе проблемы, описанные в вопросе не может быть решена напрямую.

Проблема 1: Передача wideString, который является локальным массивом, в функцию будет означать его тип распадается на wchar_t* RValue, и что не может быть связанно с wchar_t *& неконстантной ссылки. Другими словами, вы не можете изменить функцию адреса локального массива. Приведение его в указатель не изменяет этот факт, а компилятор ошибочен, когда принимает это решение.

Задача 2: Аналогичным образом, передача адреса nCodepoint по ссылке невозможна, так как этот адрес не может быть изменен. Единственным решением является сохранение адреса в отдельный указатель, а затем передать, что:

unsigned long *pCodepoint = &nCodepoint; 
Utf8toUtf32(pIter,PIter+5,pCodepoint,pCodepoint+1); 

(Другой редактирование jogojapan)

Если вы хотите передать по ссылке, но вы хотите чтобы сделать функцию достаточно гибкой, чтобы принять не-ссылочные параметры, а также, вы можете предоставить перегруженные определения шаблона:

/* Using C++11 code for convenience. Rewriting in C++03 is easy. */ 
#include <type_traits> 

template <typename T> 
using noref = typename std::remove_reference<T>::type; 

template <typename Iter8, typename Iter32> 
int Utf8toUtf32 (Iter8 &from, const Iter8 from_end, Iter32 &dest, const Iter32 dest_end) 
{ 
    return 0; 
} 

template <typename Iter8, typename Iter32> 
int Utf8toUtf32 (Iter8 &from, const Iter8 from_end, noref<Iter32> dest, const Iter32 dest_end) 
{ 
    noref<Iter32> p_dest = dest; 
    return Utf8toUtf32(from,from_end,p_dest,dest_end); 
} 

template <typename Iter8, typename Iter32> 
int Utf8toUtf32 (noref<Iter8> from, const Iter8 from_end, Iter32 &dest, const Iter32 dest_end) 
{ 
    noref<Iter8> p_from = from; 
    return Utf8toUtf32(p_from,from_end,dest,dest_end); 
} 

template <typename Iter8, typename Iter32> 
int Utf8toUtf32 (noref<Iter8> from, const Iter8 from_end, noref<Iter32> dest, const Iter32 dest_end) 
{ 
    noref<Iter8> p_from = from; 
    noref<Iter32> p_dest = dest; 
    return Utf8toUtf32(p_from,from_end,p_dest,dest_end); 
} 

вы можете назвать это со всеми видами с очетание яв ляю из lvalues ​​и rvalues:

int main() 
{ 
    char input[]  = "hello"; 
    const char *p_input = input; 
    unsigned long dest; 
    unsigned long *p_dest = &dest; 
    std::string input_str("hello"); 

    Utf8toUtf32(input,input+5,&dest,&dest+1); 
    Utf8toUtf32(p_input,p_input+5,&dest,&dest+1); 

    Utf8toUtf32(input,input+5,p_dest,p_dest+1); 
    Utf8toUtf32(p_input,p_input+5,p_dest,p_dest+1); 

    Utf8toUtf32(begin(input_str),end(input_str),p_dest,p_dest+1); 
    Utf8toUtf32(begin(input_str),end(input_str),&dest,&dest+1); 

    return 0; 
} 

Но имейте в виду: При пропускании RValue (например, массив или выражение, как &local_var), то вызов будет работать и там не будет неопределенное поведение, но, конечно, адрес локальной переменной или массива, конечно, все равно не изменится. Таким образом, вызывающий не сможет в этой ситуации выяснить, сколько символов функция могла обработать.

+0

Как ранее отмечалось, '_from' и' _dest' ДОЛЖНЫ быть переданы по ссылке. – Twifty

+0

@ Waldermort Почему? Если вы хотите уведомить пользователя о функции, в которой позиция закончилась, вы можете вернуть их как итераторы. Я не вижу причины для изменения аргументов, переданных функции. – dyp

+0

прочитайте выше комментарии.Обновлены оба параметра '_dest' и' from', нет смысла возвращать их как пару, а также возвращать возвращаемый тип int. – Twifty