2

在尝试在库中添加对 UTF-8 语言环境的支持时,我将类型添加std::wstringboost::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 >]'
< SNIP SNIP >
Cursor.H:84: 从这里实例化 /opt/TWWfsw/libboost147/include/boost/variant/detail/variant_io.hpp:64: 错误: 'operator<<' in '((const boost::detail ::变体::打印机 >>)this)->boost::detail::variant::printer > >::out_ <<operand'
/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]
等。

此示例使用带有 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在变体模板参数中,则会导致相同的错误。

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

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

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

4

2 回答 2

3

你的问题不是boost::variant不能同时处理stringwstring。问题是wcout无法处理std::string。有关这方面的更多信息,请参阅为什么 wcout << ""; 可以,但是 wcout << string(); 不是?

于 2013-05-02T23:40:47.267 回答
1

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

注意std::cout << std::string()andstd::wcout << std::wstring()是有效的,而std::wcout << std::string()and std::wcout << std::string()are not。这仅仅是由于流运算符的定义(请参阅为什么 wcout << ""; 可以但 wcout << string(); 不行?)除非您提供处理此问题的重载,否则根本不可能.

这同样适用于变体:如果您尝试将 a variant<std::string>intostd::wcout或 a variant<std::wstring>into流式传输到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。为此,您可以编写包装器,接受 astd::string并在将其传递给实际 API 之前进行转换。

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

于 2018-03-29T08:16:33.157 回答