boost :: variant无法一起处理字符串和wstring

时间:2013-05-02 23:18:47

标签: c++ wstring boost-variant

尝试在库中添加对UTF-8语言环境的支持时,我补充道 保存值的std::wstring类型boost::variant

此时,我开始在boost::variant内发现错误:

  

Blockquote / opt / TWWfsw / libboost147 / include / boost / variant / detail / variant_io.hpp:在成员函数'void boost :: detail :: variant :: printer :: operator()(const T&)const [使用T = std :: basic_string,std :: allocator>,OStream = std :: basic_ostream>]':
  /opt/TWWfsw/libboost147/include/boost/variant/variant.hpp:858:从'typename Visitor :: result_type boost :: detail :: variant :: invoke_visitor :: internal_visit(T&,int)实例化[with T = const std :: basic_string,std :: allocator&gt ;, Visitor = boost :: detail :: variant :: printer> >]'
  < SNIP SNIP>
  Cursor.H:84:从这里实例化   /opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp:64:错误:不匹配'operator<<'在'((const boost :: detail :: variant :: printer>> )this) - > boost :: detail :: variant :: printer> > :: out_<<操作数“
  / opt / TWWfsw / gcc44 / include / c ++ / ostream:108:注意:候选人是: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]
等。

此示例使用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,它也不会编译。

2 个答案:

答案 0 :(得分:3)

您的问题不是boost::variant无法处理stringwstring。问题是wcout无法处理std::string。有关此问题的详细信息,请参阅Why is it that wcout << ""; is OK but wcout << string(); is not?

答案 1 :(得分:1)

以@John Bandela的回答为基础:他实际上是正确的。我只是解释一下罪魁祸首。

请注意std::cout << std::string()std::wcout << std::wstring()有效,而std::wcout << std::string()std::wcout << std::string()则不然。这只是由于流操作符的定义(参见Why is it that wcout << ""; is OK but wcout << string(); is not?)除非您提供处理此操作的重载,否则根本不可能。

同样适用于变体:如果您尝试将variant<std::string>流式传输到std::wcout或将variant<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中)并且只在边界点转换。