This is the C++ program:
#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
}
I want to use implicit conversion from const char *
to string
in the call of generic STL function accumulate
. I know that conversion from const char *
to string is not explicit, so we can pass const char *
parameter to calls in which a string
type is required. This can be proved by the above test_string
function. But when I did the same thing in accumulate
, the compiler complain:
error C2440: '=': cannot convert from 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>' to 'const char *'
The code works only when I replaced ""
with string("")
. I don't understand why the implicit conversion works for my custom function but does not work in accumulate
. Can you explain that? Thanks a lot.
PS: I am using Visual Studio 2015.
std::accumulate is declared as
template< class InputIt, class T >
T accumulate( InputIt first, InputIt last, T init );
That means the template argument T
is deduced from the argument passed in (i.e. ""
). Then it'll be const char*
. On the other hand, how could the compiler perform the implicit conversion? Which type should be the target type?
You can pass a std::string
explicitly, or specify the template argument explicitly. e.g.
// 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(), "");
Take a look at the 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;
}
When you call the function the way you did, InputIt
will be deduced as a vector<string>::const_iterator
and T
will be deduced to be a const char*
. As you can see here in the for loop, the line of code that does the "accumulation" is this
init = init + *first
Here on the right hand side of the assignment *first
will evaluate to a string&
and init
will evaluate to a const char*
. Then you will be using the std::string::operator+
which will concatenate the const char*
and the std::string
instance to get a std::string
back. And then you are trying to assign a std::string
to a const char*
variable. This is not legal.
This will not work as std::string
objects are not implicitly convertible or assignable to const char*
, the reverse is true however.
To fix this change your code to the following (note that I postfixed the string literal with a s
, which is C++14 syntax for a user defined literal (which in this case evaluates to a 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);
}
Also as noted in the comments, change void main()
to int main()
. For more see What should main() return in C and C++?
I don't understand why the implicit conversion works for my custom function but does not work in accumulate. Can you explain that?
Implicit conversion isn't even attempted, std::accumulate simply tries to accumulate by adding instances of std::string
to a sum that's initialized as auto sum = "";
and you get the same error that you would get in this case:
std::string s = "abc";
const char* sum = "";
sum = sum + abc; // <-- error
The code works only when I replaced "" with string("")
Because this way internally accumulator's type is std::string
and everything works as intended. You may as well do this:
string sum = accumulate(v.cbegin(), v.cend(), ""s);
As a side note, it should be int main() { ... }
, not void main
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With