【问题标题】:Why do I have to use std::string() even for "" to satisfy a template arguments?为什么我必须使用 std::string() 即使是 "" 来满足模板参数?
【发布时间】:2016-11-15 06:19:43
【问题描述】:

我写了一个非常简单的模板来标记一个字符串,如下所示。

但是,我在调用该函数时遇到问题,我不能将 C 字符串用于 delimiterstrim_string 参数。这些必须是std::string(或任何类型的字符串StringT,即std::wstring)。

所以以下失败:

std::vector<std::string> tokens;
std::string str = "This string, it will be split, in 3.";
int count = tokenize_string(tokens, str, ",", true, " ");

为了解决这个问题我必须写:

std::vector<std::string> tokens;
std::string str = "This string, it will be split, in 3.";
int count = tokenize_string(tokens, str,
                       std::string(","), true, std::string(" "));

在这种情况下,有没有办法避免在标准 C 字符串周围使用 std::string()?

我在使用 g++ 时遇到的错误如下所示:

/home/snapwebsites/snapwebsites/snapmanagercgi/daemon/snapmanagerdaemon.cpp: In member function ‘void snap_manager::manager_daemon::init(int, char**)’:
/home/snapwebsites/snapwebsites/snapmanagercgi/daemon/snapmanagerdaemon.cpp:103:71: error: no matching function for call to ‘tokenize_string(std::vector<std::__cxx11::basic_string<char> >&, const string&, const char [2], bool, const char [2])’
         snap::tokenize_string(f_bundle_uri, bundle_uri, ",", true, " ");
                                                                       ^
In file included from /home/snapwebsites/snapwebsites/snapmanagercgi/daemon/snapmanagerdaemon.cpp:35:0:
/home/snapwebsites/BUILD/dist/include/snapwebsites/tokenize_string.h:46:8: note: candidate: template<class StringT, class ContainerT> size_t snap::tokenize_string(ContainerT&, const StringT&, const StringT&, bool, const StringT&)
 size_t tokenize_string(ContainerT & tokens
        ^
/home/snapwebsites/BUILD/dist/include/snapwebsites/tokenize_string.h:46:8: note:   template argument deduction/substitution failed:
/home/snapwebsites/snapwebsites/snapmanagercgi/daemon/snapmanagerdaemon.cpp:103:71: note:   deduced conflicting types for parameter ‘const StringT’ (‘std::__cxx11::basic_string<char>’ and ‘char [2]’)
         snap::tokenize_string(f_bundle_uri, bundle_uri, ",", true, " ");
                                                                       ^

模板:

template < class StringT, class ContainerT >
size_t tokenize_string(ContainerT & tokens
                     , StringT const & str
                     , StringT const & delimiters
                     , bool const trim_empty = false
                     , StringT const & trim_string = StringT())
{
    for(typename StringT::size_type pos(0), last_pos(0); last_pos < str.length(); last_pos = pos + 1)
    {
        pos = str.find_first_of(delimiters, last_pos);

        // no more delimiters?
        //
        if(pos == StringT::npos)
        {
            pos = str.length();
        }

        char const * start(str.data() + last_pos);
        char const * end(start + (pos - last_pos));

        if(start != end                 // if not (already) empty
        && !trim_string.empty())        // and there are characters to trim
        {
            // find first character not in trim_string
            //
            start = std::find_if_not(
                  start
                , end
                , [&trim_string](auto const c)
                  {
                      return trim_string.find(c) != StringT::npos;
                  });

            // find last character not in trim_string
            //
            if(start < end)
            {
                reverse_cstring<typename StringT::value_type const> const rstr(start, end);
                auto p = std::find_if_not(
                      rstr.begin()
                    , rstr.end()
                    , [&trim_string](auto const c)
                      {
                          return trim_string.rfind(c) != StringT::npos;
                      });
                end = p.get();
            }
        }

        if(start != end     // if not empty
        || !trim_empty)     // or user accepts empty
        {
            tokens.push_back(typename ContainerT::value_type(start, end - start));
        }
    }

    return tokens.size();
}

【问题讨论】:

  • tokensstr的类型有哪些?
  • 尝试在字符串前面加上一个 S 或一个小写的 s(我忘记了这些文字的真实含义)或附加。我不确定语法。可以是S","s","","S","s(试试看哪个不会出现语法错误)
  • @PaulStelian 是后记小写 s,你需要 using namespace std::literals 或类似的东西。
  • @PaulStelian it's ","s
  • @PaulStelian,你是对的。这样可行。虽然据我回忆,这与std::string(...) 的作用相同,但它只是使用 C++11 中的一个新特性来隐藏它。不过,我想在这种情况下这很实用!

标签: c++ string templates c++11


【解决方案1】:

规则是当你有三个StringT const &amp;参数时,StringT独立于对应的参数推导,推导的类型必须匹配。

你可以

  • 如果容器的值类型应该是正确的字符串类型,则对这三个都使用typename ContainerT::value_type;或
  • 从三个StringT-take参数中的两个中扣除StringT

    • 在调用站点通过使用 braced-init-lists 使后面的两个参数成为非推断上下文:

      int count = tokenize_string(tokens, str, {","}, true, {" "});
      
    • 或者在函数模板本身中,通过将后两个StringT 参数包装到非推导上下文中:

      template < class StringT, class ContainerT >
      size_t tokenize_string(ContainerT & tokens
                           , StringT const & str
                           , typename std::decay<StringT>::type const & delimiters
                           , bool const trim_empty = false
                           , typename std::decay<StringT>::type const & trim_string = StringT())
      
  • 或者为每个参数取不同的类型参数,然后在函数模板主体中协调它们。

【讨论】:

    【解决方案2】:

    您可以使用字符串文字(如 Paul Stelian 和其他人所建议的那样),或者您可以显式调用 tokenize_string() 的第一个模板参数。

    举个例子,这样

    int count = tokenize_string<std::string>(tokens, str, ",", true, " ");
    

    【讨论】:

      【解决方案3】:

      我认为,如果您允许分隔符参数与 str 参数不同,您的函数将起作用。基本上它试图根据两种不同的类型来决定StringT 是什么,当然它不能。因此,如果您这样做,它应该可以工作:

      template < class StringT,  class DelimT, class TrimT class ContainerT >
      size_t tokenize_string(ContainerT & tokens
                           , StringT const & str
                           , DelimT const & delimiters
                           , bool const trim_empty = false
                           , TrimT const & trim_string = StringT())
      

      这将允许编译器将StringT 推断为std::stringDelimT 推断为char[2] 或任何静态字符串。

      delimeters 仅在您对find_first_of 的调用中使用,并且该函数将接受静态字符串...所以一旦您让编译器正确推断类型,一切都应该正常工作。您不需要隐式或以其他方式转换为 StringT

      TrimT 可能不需要分开...您可以将其保留为 StringT

      其他答案没有错误。他们也会工作。但我认为你需要做的就是让编译器为这两个参数推导出两种不同的类型......然后不需要专门化或重载。

      【讨论】:

      • 这是 T.C. 答案中的第三种可能性。我也可以在某个时候尝试一下。虽然我的字符串也可以是 UTF8 或 UTF32 ......所以我必须小心如何将它们相互比较。
      • 是的,刚刚注意到 TC 也给出了这个答案,只是去掉了细节。
      【解决方案4】:

      这是因为您正在使用模板。如果您没有明确声明函数,C++ 无法推断出字符串文字(在数据类型术语中通常表示为 const char *)意味着隐式转换std::string 对象获取std::string 对象。

      最好的解决方案是编写两个包装函数,为函数定义的第一部分显式定义字符串类型。

      template <class ContainerT >
      size_t tokenize_string(ContainerT & tokens
                           , std::string const & str
                           , std::string const & delimiters
                           , bool const trim_empty = false
                           , std::string const & trim_string = std::string ()) {
          return tokenize_string_detail(tokens, str, delimiters, trim_empty, trim_string); //your original function
      }
      
      template <class ContainerT >
      size_t tokenize_string(ContainerT & tokens
                           , std::wstring const & str
                           , std::wstring const & delimiters
                           , bool const trim_empty = false
                           , std::wstring const & trim_string = std::wstring ()) {
          return tokenize_string_detail(tokens, str, delimiters, trim_empty, trim_string); //your original function
      }
      

      我还会考虑简单地放弃对 std::wstring 的支持,因为这是对 unicode 支持的一次令人难以置信的失败尝试,而且总的来说,boost 中的各种实用程序可以更好地提供 unicode 支持。

      【讨论】:

      • 这不一定是一个好的解决方案。它可能适用于大多数情况,但在许多情况下它不会。例如,当用户覆盖 char_traits 时。必须记住std::stringstd::basic_string的一个实例化typedef,一个模板。
      • @CrazyEddie 但是如果他们只是传入字符串文字,用户就不会修改char_traits。如果目标是处理字符串文字而不需要添加代码以使编译器接受它,那么这将工作得很好。如果他们真的非常需要代码的完全通用版本可见,那么他们只需要将 tokenize_string_detail 公开。
      • @Xirema,想过专业化,但不喜欢这个想法。正如 Crazy Eddie 所说,您可能有不同的实例化(我有一个 utf32 实例,这使得在不只是使用 ASCII 时更容易处理字符)。然而,T.C.答案是正确的。我实际上可以使用typename ContainerT::value_type,它解决了这个问题。
      猜你喜欢
      • 2013-05-31
      • 1970-01-01
      • 2017-11-25
      • 2016-01-31
      • 2021-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多