Boost::variant不能同时处理string和wstring

boost::variant can not handle string and wstring together

本文关键字:string wstring 处理 variant 不能 Boost      更新时间:2023-10-16

在尝试在库中添加对UTF-8区域设置的支持时,我添加了

将类型std::wstring转换为保存值的boost::variant

在这一点上,我开始得到错误的东西在boost::variant:

Blockquote/opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp: In member function 'void boost::detail::variant::printer<OStream>::operator()(const T&) const [with T = std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >, OStream = std::basic_ostream<char, std::char_traits<char> >]':<BR>
/opt/TWWfsw/libboost147/include/boost/variant/variant.hpp:858:   instantiated from 'typename Visitor::result_type boost::detail::variant::invoke_visitor<Visitor>::internal_visit(T&, int) [with T = const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >, Visitor = boost::detail::variant::printer<std::basic_ostream<char, std::char_traits<char> > >]'<BR>
< SNIP SNIP ><BR>
Cursor.H:84:   instantiated from here
/opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp:64: error: no match for 'operator<<' in '((const boost::detail::variant::printer<std::basic_ostream<char, std::char_traits<char> > >*)this)->boost::detail::variant::printer<std::basic_ostream<char, std::char_traits<char> > >::out_ << operand'<BR>
/opt/TWWfsw/gcc44/include/c++/ostream:108: note: candidates are: std::basic_ostream<_CharT, _Traits>& std::basic_ostream<_CharT, _Traits>::operator<<(std::basic_ostream<_CharT, _Traits>& (*)(std::basic_ostream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]<BR>etc.

本例使用boost-1.47 w/g++ 4.4。

#include <string>
#include <iostream>
#include <boost/variant.hpp>
#define NO_STRING 1
int main (int argc, char** argv)
{
#if defined(NO_STRING)
  boost::variant<int, std::wstring> v;
#else
  boost::variant<int, std::wstring, std::string> v;
#endif
  v = 3;
  std::wcout << v << std::endl;
  std::wstring T(L"wide char literal");
  v = T;
  std::wcout << v << std::endl;
  return 0;
}

程序将输出:

3
宽字符字面值

但如果去掉#define,并且stringwstring都在不同的模板参数中,则产生相同的错误。

我的问题是,我可以创建一些东西来满足这个缺失的定义,比如模板专门化吗?

可以定义一个将宽字符串转换为窄字符串的变体访问器吗?(不是一般的解决方案,但缩小在我的情况下会工作)

问题来自于在定义了两个字符串时使用<<和变体。即使我只通过变量输出int,它也不会编译。

你的问题不是boost::variant不能处理stringwstring。问题是wcout不能处理std::string。有关这方面的更多信息,请参阅为什么会出现这种情况<<";还可以,但是我要离开<<字符串();不是吗?

基于@John Bandela的回答:他实际上是对的。

我要多解释一下这个罪魁祸首。

注意std::cout << std::string()std::wcout << std::wstring()是有效的,而std::wcout << std::string()std::wcout << std::string()是无效的。这仅仅是由于流操作符的定义(参见为什么wcout <<";还可以,但是我要离开<<字符串();不是吗?)这是不可能的,除非你提供一个重载来处理它。

同样适用于变量:如果您尝试将variant<std::string>流到std::wcoutvariant<std::wstring>流到std::cout,您将得到相同的错误。

问题的根本在于:应用于变量的操作(访问、流操作符)必须支持所有可能包含的类型。这是由于实现类似于(伪代码):

void variant::visit(T operation){
  if(contained is T1) operation(static_cast<T1>(contained));
  else if(contained is T2) operation(static_cast<T2>(contained));
  ...
}

所以你的问题的解决方案是:自己实现所有类型的操作:

#include <string>
#include <iostream>
#include <boost/variant.hpp>
std::wstring stringToWString(const std::string& v){
    // Only works for single-byte strings. Do it better!
    std::wstring result(v.begin(), v.end());
    return result;
}
struct visitor: boost::static_visitor<void>{
    template<typename T>
    void operator()(const T& v) const { std::wcout << v; }
    void operator()(const std::string& v) const { std::wcout << stringToWString(v); }
};
int main (int argc, char** argv)
{
  boost::variant<int, std::wstring, std::string> v;
  v = 3;
  boost::apply_visitor(visitor(), v);
  std::wcout << std::endl;
  std::wstring T(L"wide char literal");
  v = T;
  boost::apply_visitor(visitor(), v);
  std::wcout << std::endl;
  v = std::string("Narrow string");
  boost::apply_visitor(visitor(), v);
  std::wcout << std::endl;
  return 0;
}
http://coliru.stacked-crooked.com/a/35e156f38208af1e

最后注意:使用std::wstring不支持UTF-8 ! UTF-8可以存储在常规的std::string中。很少有地方需要将其转换为其他内容。其中之一是处理WinAPI。为此,您可以编写包装器,它接受std::string并在将其传递给实际API之前进行转换。

所以一般的建议:在你的程序中使用UTF-8(在std::string中),只在边界点转换