Подтвердить что ты не робот

Почему неявное преобразование не работает в накоплении?

Это программа на С++:

#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.

4b9b3361

Ответ 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