2

I recently gave an answer to this question on how to get Python-like string repeats, e.g. "hello" * 2 gives "hellohello".

I won't repeat the definition here, but the function declaration is:

std::string repeat(std::string str, const std::size_t n);

and can of course can be used like:

std::cout << repeat("helloworld", 2) << std::endl;

To get closer to the Python version, I thought I'd overload operator*. Ideally I'd use a universal reference to avoid the additional std::string move, but operators must use a user-defined type. So I tried this instead:

#include <type_traits> // std::enable_if_t, std::is_integral
#include <utility>     // std::move

template <typename T, typename = std::enable_if_t<std::is_integral<T>::value>>
std::string operator*(std::string str, const T n)
{
    return repeat(std::move(str), static_cast<std::size_t>(n));
}

Now I can do this:

std::cout << (std::string("helloworld") * 2) << std::end;

and this:

std::cout << operator*("helloworld", 2) << std::endl;

but not this:

std::cout << ("helloworld" * 2) << std::endl;
// error: invalid operands to binary expression ('const char *' and 'int')

Why not?

Community
  • 1
  • 1
Daniel
  • 8,179
  • 6
  • 31
  • 56
  • 2
    If you have c++14, try `("helloworld"s * 2)`. – aschepler Dec 17 '15 at 21:41
  • FWIW, I asked something [similar](http://stackoverflow.com/questions/10591262/why-doesnt-stdstring-define-multiplication-or-literals) a good while back. Granted it's two questions in one and one of those is actually in C++14. – chris Dec 17 '15 at 21:41
  • @aschepler I think marking this as duplicate is a little harsh. I hadn't seen that question, but I think this question is different in that I'm asking why the prefix version of operator works vs the infix version. Which the other question doesn't highlight. – Daniel Dec 17 '15 at 21:55
  • @Daniel: Marking a question as a duplicate isn't a condemnation of the new question; it's merely an attempt to ensure that there aren't multiple discussions of the same issue all over the site. Also, even though my question doesn't explicitly highlight prefix vs. infix, the reasoning is exactly the same. – R_Kapp Dec 17 '15 at 21:59
  • @R_Kapp It wasn't at all obvious to me there was any difference between prefix vs infix (I thought infix was simply syntactic sugar), and reading the answer to your question doesn't answer that either. Perhaps I'll edit the question to emphasis this. – Daniel Dec 17 '15 at 22:03
  • I've just come across [this](http://stackoverflow.com/questions/25352640/infix-vs-prefix-syntax-name-lookup-differences) question which addresses prefix vs infix. – Daniel Dec 17 '15 at 22:06

2 Answers2

5

When you define an overloaded operator, at least one of the operands must be of a user-defined type. For pre-defined types, all operators are either pre-defined, or else prohibited.

Where you've explicitly converted to std::string the string ctor that takes a char const * as its parameter can/will be used to convert the literal to an std::string, but without that, the compiler can't/won't do the conversion.

Likewise, when you invoke the operator more explicitly as operator*("helloworld", 2), the compiler "knows" that it needs to convert the string literal to a type supported by an overload of operator *, so it (basically) enumerates all the types to which a string literal can be converted, and then sees if it can find an operator * that fits one of those types. If it finds more than one, it does (if memory serves) normal overload resolution on the candidate operator * implementations to decide which to use.

With just the expression string-literal * int, however, both types are built in, so it only examines the built-in operators. Since none of them fits, the expression is prohibited.

Note that with a current compiler, you could use a suffix of s on the string literal to create a std::string:

#include <string>

std::cout << "helloworld"s * s << "\n";
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • I'm still a little confused why `operator*("helloworld", 2)` works, but `"helloworld" * 2` doesn't. I haven't explicitly converted the string literal into a `std::string`, and isn't the latter converted by the compiler into the former (i.e. aren't prefix and postfix `operator` equivalent)? – Daniel Dec 17 '15 at 21:50
  • @Daniel: I've added a little more explanation that I hope clarifies the situation a bit. – Jerry Coffin Dec 17 '15 at 21:56
3

Because "helloworld" is not a std::string, it's a char array.

Emil Laine
  • 41,598
  • 9
  • 101
  • 157
  • No it's not, it's a [string literal](http://en.cppreference.com/w/cpp/language/string_literal). And this doesn't explain why the prefix version works. – Daniel Dec 17 '15 at 21:42
  • 1
    @Daniel From your link: "The ___type___ of an unprefixed string literal is `const char[]`". – Emil Laine Dec 17 '15 at 21:45
  • 1
    Can I reclaim some pride by noting you missed a `const`? :p – Daniel Dec 17 '15 at 22:12