2014-10-27 3 views
3

Это FAQ, но я не смог найти удовлетворительного ответа. В моем проекте мы поддерживаем std::string и теперь также должны поддерживать широкие строки. Поэтому мы хотим, чтобы перейти к basic_string, но потом, все перестанет работать хорошо и параметры должны быть прописаны в явном виде:basic_string <CharT> vs. CharT *

#include <string> 

template <typename CharT, typename Traits, typename Allocator> 
void 
foo(const std::basic_string<CharT, Traits, Allocator>&) 
{} 

template void foo(const std::string&); 

// template void 
// foo<char, std::char_traits<char>, std::allocator<char>>(const std::string&); 

void bar(const std::string& s) 
{} 

int main() 
{ 
    bar("abc"); 
    foo<char, std::char_traits<char>, std::allocator<char>>("def"); 
    foo("def"); 
} 

ОК, она не для хорошо известной причине:

clang++-mp-3.5 -Wall -std=c++11 foo.cc 
foo.cc:20:3: error: no matching function for call to 'foo' 
    foo("def"); 
    ^~~ 
foo.cc:5:1: note: candidate template ignored: could not match 
     'basic_string<type-parameter-0-0, type-parameter-0-1, type-parameter-0-2>' 
     against 'char const[4]' 
foo(const std::basic_string<CharT, Traits, Allocator>&) 
^ 

Что Я не понимаю, почему он работает для bar? Почему для решения этой проблемы недостаточно явного создания экземпляра foo для char (либо с явными параметрами шаблона, либо с вычитанием)?

Кажется, что это означает, что вместо того, чтобы использовать шаблоны и basic_string в облученной API, мы будем использовать его в качестве детали реализации, но выставить пользователь перегрузок для std::string, std::wstring и т.д., которая является позором.

Спасибо!

+0

е xclicit версия 'foo' работает с clang3.5, только ошибка с выводом параметра (последняя строка), потому что она не может выводить параметр. – Drax

ответ

5

Для bar("abc") существует неявное преобразование от char const[4] до std::string. foo отличается от bar тем, что на самом деле это не функция, а функция template. Его аргументы шаблона должны быть известны, чтобы построить правильную функцию.

Первый вызов foo явно дает аргументы шаблона, поэтому он строит функцию, которая выглядит следующим образом:

void foo(const std::basic_string<char, std::char_traits<char>, std::allocator<char>>&); 

неявного преобразования пинки в и все в порядке.

Третий вызов не предоставляет аргументов шаблона, поэтому компилятор должен выяснить тип CharT, Traits и Allocator от типа char const[4]. Этот тип не несет с собой эту информацию, поэтому вывод не выполняется, и разрешение перегрузки не может найти правильную функцию.

+0

Я думаю, я просто очень расстроен тем, что перегруженные функции и явно созданные шаблоны функций не ведут себя одинаково. Благодаря! – akim

0

Лучший обходной путь, кажется, чтобы обеспечить перегруженные нешаблонном для std::string и std::wstring этот делегат на шаблон функции (Demo at Coliru):

template <typename CharT, typename Traits, typename Allocator> 
void foo(const std::basic_string<CharT, Traits, Allocator>&) 
{} 

inline void foo(const std::string& u) { foo<>(u); } 
inline void foo(const std::wstring& u) { foo<>(u); } 
+0

* «Лучшее обходное решение похоже» * Почему бы не использовать строковые суффиксы 'std :: wstring'? – dyp

+0

@dyp: И что, когда параметр является строкой C-стиля, которая не является литералом? –

+0

@BenVoigt Ах, хорошая точка. Я только рассмотрел тестовый пример OP. – dyp

4

делает эту работу за вас:

template <typename StringT> 
void foo(const StringT& the_string) 
{ 
    typedef decltype(the_string[0]) CharT; 
    // do the work 
} 

Это может вывести StringT как std::string, std::wstring, const char[N], const wchar_t[N], std::vector<char> и т. Д.И если вы хотите неявные преобразования строк C-типа в StD :: строки заранее, так что вы можете использовать функции-членов, общие для всех коллекций STL, добавить перегрузку пересылки, которая улавливает массивы:

template <typename CharT, size_t N> 
void foo(const CharT (&char_array_or_literal)[N]) 
{ 
    foo(std::basic_string<CharT>(char_array_or_literal)); 
} 

А может быть, другим для символьных указателей:

template <typename CharT> 
void foo(const CharT* char_ptr) 
{ 
    foo(std::basic_string<CharT>(char_ptr)); 
} 

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

template <typename CharT, typename Traits, typename Allocator> 
void foo(const std::basic_string<CharT, Traits, Allocator>& the_string) 
{ 
    // the real work is done here 
} 

template <typename StringLikeT> 
void foo(const StringLikeT& the_string_like_thing) 
{ 
    typedef decltype(the_string_like_thing[0]) CharT; 
    // this turns string literals, arrays, pointers, vectors, std::array, all into basic_string 
    foo(basic_string<CharT>(&the_string_like_thing[0])); 
} 
+0

Я думаю, что этот ответ ясно показывает, что происходит - код из вопроса шаблонизирован по неправильной вещи (подробности basic_string), а не по понятию строки. С решением в этом ответе может быть передан даже std :: array и std :: vector . И немного вне темы, подумайте о том, чтобы придерживаться std :: string только с условием, что это кодировка UTF-8 и только конвертировать в широкие строки для API, которые этого требуют. –

+0

Я был обеспокоен тем, что использовал аргумент «non constrained», такой как 'StringT': слишком много других вещей могло бы проникнуть. Но верно, что с некоторой проверкой того, что такое 'StringT', это может быть очень приятно. Благодаря! – akim

Смежные вопросы