【问题标题】:boost::variant can not handle string and wstring togetherboost::variant 不能同时处理字符串和 wstring
【发布时间】:2013-04-27 06:55:31
【问题描述】:

在尝试在库中添加对 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 [with 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 >, Visitor = boost::detail::variant::printer >]'

Cursor.H:84:从这里实例化 /opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp:64: 错误: 'operator >)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>&)) [with _CharT = char, _Traits = std::char_traits]

此示例使用带有 g++ 4.4 的 boost-1.47。

#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都在变量模板参数中,同样的错误结果。

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

也许定义一个将宽字符串转换为窄字符串的变体访问者? (不是一般的解决方案,但在我的情况下可以缩小范围)

问题来自在定义两个字符串时将&lt;&lt; 与变体一起使用。即使我只通过变体输出int,它也不会编译。

【问题讨论】:

    标签: c++ wstring boost-variant


    【解决方案1】:

    你的问题不是boost::variant 不能同时处理stringwstring。问题是wcout 无法处理std::string。有关这方面的更多信息,请参阅Why is it that wcout << ""; is OK but wcout << string(); is not?

    【讨论】:

    • 因此,即使变体不包含 std::string 值,也不能在“wcout 严重限制了 boost::variant 的用处吗?本质上,我必须拥有所有字符串都很宽或所有字符串都很窄的应用程序。
    • 这不是使用“wcout”的问题,如果我将程序修改为使用std::cout并输出int值的变体,也会出现同样的错误。
    • 约翰 - 你的答案是有效的 - 但这不是这个问题的意义所在。问题在于 boost::variant 的实现,在宽和窄字符串都是有效类型的情况下,它似乎无法处理“operator
    【解决方案2】:

    基于@John Bandela 的回答:他实际上是正确的。我将稍微解释一下罪魁祸首。

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

    这同样适用于变体:如果您尝试将variant&lt;std::string&gt; 流式传输到std::wcout 或将variant&lt;std::wstring&gt; 流式传输到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

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

    所以一般建议:在程序的任何地方都使用 UTF-8(在 std::string),并且只在边界点进行转换。

    【讨论】:

    • 自从我问了这个问题后,我对编码有了更多的了解,现在我是 UTF-8 的狂热爱好者。如果您有兴趣,请阅读utf8everywhere.org
    猜你喜欢
    • 1970-01-01
    • 2016-10-16
    • 1970-01-01
    • 1970-01-01
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-03
    相关资源
    最近更新 更多