【问题标题】:Why does implicit conversion not work in accumulate?为什么隐式转换在累积中不起作用?
【发布时间】:2017-06-16 05:48:21
【问题描述】:

这是 C++ 程序:

#include <iostream>
#include <vector>
#include <numeric>
using namespace std;

int test_string(const string & str) {
    return str.size();
}

void main() {
    test_string("");                                     //can compile
    vector<string> v;
    string sum = accumulate(v.cbegin(), v.cend(), "");   //cannot compile
}

我想在通用 STL 函数 accumulate 的调用中使用从 const char *string 的隐式转换。我知道从const char * 到字符串的转换不是明确的,所以我们可以将const char * 参数传递给需要string 类型的调用。这可以通过上面的test_string函数来证明。但是当我在accumulate 做同样的事情时,编译器抱怨:

error C2440: '=': cannot convert from 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' to 'const char *'

该代码仅在我将 "" 替换为 string("") 时才有效。我不明白为什么隐式转换适用于我的自定义函数但不适用于accumulate。你能解释一下吗?非常感谢。

PS:我使用的是 Visual Studio 2015。

【问题讨论】:

  • void main() -- 不,应该是int main()
  • "我想使用从 const char *string 的隐式转换"...但是编译器不知道你“想要”那个。表达式accumulate(v.cbegin(), v.cend(), "") 中绝对没有任何内容表明应将"" 隐式转换为std::string。为什么您希望编译器执行转换?您希望编译器如何确定转换是必要的?

标签: c++ string templates implicit-conversion


【解决方案1】:

std::accumulate 被声明为

template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );

这意味着模板参数T是从传入的参数推导出来的(即"")。那么它将是const char*。另一方面,编译器如何执行隐式转换?目标类型应该是哪种类型?

您可以显式传递std::string,或显式指定模板参数。例如

// pass a std::string exactly
string sum = accumulate(v.cbegin(), v.cend(), string(""));

// T is specified as std::string explicitly
// "" will be implicitly converted to std::string
string sum = accumulate<decltype(v.cbegin()), string>(v.cbegin(), v.cend(), "");

【讨论】:

  • 在 C++11 及更高版本中,string("") 也可以替换为 ""s
  • @user5280911 是的
  • 谢谢松元瑶,你准确的看出了我的错误:我误以为第三个参数的类型被声明了,因此像通常的函数一样被确定,所以没有进行隐式转换。
【解决方案2】:

看看possible implementation from cppreference

template<class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init)
{
    for (; first != last; ++first) {
        init = init + *first;
    }
    return init;
}

当您以您的方式调用函数时,InputIt 将被推断为 vector&lt;string&gt;::const_iteratorT 将被推断为 const char*。正如您在 for 循环中看到的,执行“累积”的代码行是这样的

init = init + *first

在这里,赋值右侧的*first 将评估为string&amp;init 将评估为const char*。然后您将使用std::string::operator+,它将连接const char*std::string 实例以返回std::string。然后您尝试将std::string 分配给const char* 变量。这是不合法的。

这将不起作用,因为 std::string 对象 不能隐式转换或分配const char*,反之亦然。

要解决此问题,请将您的代码更改为以下内容(请注意,我使用 s 对字符串文字进行了后缀,这是用户定义文字的 C++14 语法(在本例中计算为 std::stringhttp://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s

int main() {
    using namespace std::string_literals;
    vector<string> v;
    string sum = accumulate(v.cbegin(), v.cend(), ""s); 
}

也作为noted in the comments,将void main() 更改为int main()。更多内容见What should main() return in C and C++?

【讨论】:

  • 这是一个非常有用的答案。我的问题不是我不知道char can't + string,而是我不明白为什么不触发隐式转换。所以我没有勾选你的答案。但我非常感谢您的帮助和所包含的丰富知识。
  • @user5280911 可能的解释是试图解释为什么没有触发隐式转换。答案中有没有您没有遵循的内容?
【解决方案3】:

我不明白为什么隐式转换适用于我的自定义函数,但不适用于累积。你能解释一下吗?

甚至没有尝试隐式转换,std::accumulate 只是尝试通过将 std::string 的实例添加到初始化为 auto sum = ""; 的总和来进行累加,并且您会得到与这种情况下相同的错误:

std::string s = "abc";
const char* sum = "";
sum = sum + abc; // <-- error

代码只有在我用 string("") 替换 "" 时才有效

因为这种方式内部累加器的类型是std::string,并且一切都按预期工作。你也可以这样做:

string sum = accumulate(v.cbegin(), v.cend(), ""s);

附带说明,应该是int main() { ... },而不是void main

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多