Почему неявное преобразование не работает в накоплении?
Это программа на С++:
#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
}
Я хочу использовать неявное преобразование от const char *
до string
в вызове общей функции STL accumulate
. Я знаю, что преобразование из 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.
Ответы
Ответ 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(), "");
Ответ 2
Взгляните на возможную реализацию из 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<string>::const_iterator
и T
будет выведено как const char*
. Как вы можете видеть здесь, в цикле for, строка кода, которая выполняет "накопление", это
init = init + *first
Здесь в правой части присваивания *first
будет оцениваться значение string&
и init
будет оцениваться как const char*
. Затем вы будете использовать std::string::operator+
, который объединит экземпляр const char*
и std::string
, чтобы получить std::string
назад. И затем вы пытаетесь назначить std::string
переменной const char*
. Это не является законным.
Это не будет работать, поскольку std::string
объекты не являются неявно конвертируемыми или присваиваемыми до const char*
, однако обратное верно.
Чтобы исправить это, измените свой код на следующее (обратите внимание, что я поставил строковый литерал с помощью s
, который является синтаксисом С++ 14 для заданного пользователем литерала (который в этом случае оценивается как std::string
). http://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);
}
Также как в комментариях, измените void main()
на int main()
. Подробнее см. Что должно main() возвращать в C и С++?
Ответ 3
Я не понимаю, почему неявное преобразование работает для моей пользовательской функции, но не работает в процессе накопления. Вы можете это объяснить?
Неявное преобразование даже не предпринимается, std:: accumulate просто пытается накапливаться, добавляя экземпляры std::string
к сумме, которая инициализируется как auto sum = "";
, и вы получите ту же ошибку, что и в этом случае:
std::string s = "abc";
const char* sum = "";
sum = sum + abc; // <-- error
Код работает только тогда, когда я заменил "на строку (" ")
Потому что этот способ внутреннего типа аккумулятора std::string
, и все работает по назначению. Вы также можете это сделать:
string sum = accumulate(v.cbegin(), v.cend(), ""s);
Как побочная заметка, она должна быть int main() { ... }
, а не void main