C++11 Operator"" with Double Parameter

C++11 operator with double parameter

What is the problem?

The problem is that the Standard forbids it. Per paragraph 13.5.8./3 of the C++11 Standard on user-defined literals:

The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the
following:

const char*
unsigned long long int
long double
char
wchar_t
char16_t
char32_t
const char*, std::size_t
const wchar_t*, std::size_t
const char16_t*, std::size_t
const char32_t*, std::size_t

Concerning a workaround, I am not sure it is needed, since the following works fine (a double gets implicitly converted to a long double, so you can pass in literals of type double):

struct str {};

str operator"" _X(long double d) {
return str();
}

int main()
{
str s = 4.0_X;
}

operator with multiple parameters

No, that is not possible.

The closest you can come would be something like:

anyType         operator<<(std::pair<arg, arg> p)
{
DoSomethingWith(p.first);
DoSomethingWith(p.second);
return (*this);
}

anyvar << std::make_pair(a1, a2);

Alternatively, you could do something much more complicated to effectively curry the call to your operator, by having anyType::operator<<(arg) return a temporary object which just holds on to its argument and implements a different tempObject::operator<<(arg) which actually does the work. You can then call that as anyvar << arg1 << arg2. I really doubt whether it is worth the trouble, aside from being a learning experience.

Something similar to that style is often used in the "builder" pattern, but using member functions rather than an explicit operator. That's useful because you can arrange to effectively make configuration arguments order-independent.

why C++ operator overloading requires having at least one parameter of class type?

  1. Is this restriction part of the language specification?

Yes, it is. Why? Well, the main reason is probably because redefining the usual operators for standard types is seen as obfuscating. Imagin overloading operator+(int,int) or operator+(int,char*). That would change the meaning of existing code!

You could argue that there are operators between standard types that are not defined, so you could safely override them, for example operator*(char*,int), but that is usually seen as rather useless.

Moreover, operator overloads must be global, (or be in the namespace of some of its members (argument dependent lookup), but with standard types there are no namespace to depend on), so interoperatibility between libraries that fancy to override them would be a nightmare.

  1. "hello" + "world": why doesn't the compiler convert the two char* "s to two strings?

Well, for one, std::operator+(const std::string&, const std::string&) is in namespace std; so it will not be found by default.

You could try calling the operator explicitly: std::operator+("hello", "world"), but alas, there are so many operator+() overloads, many of them templates, that the call is ambiguous.

So, taking all this into account, I think that a reasonable requirement is that at least one of the operators to be of a user-defined type. Think about it: the global and namespace problem is solved, ADL can be used (actually it was invented for this use) and it is impossible to redefine an operator with an existing meaning (except operator,() and operator&(), I think, but who wants to override these...).

Operator overloading with multiple arguments

If you want to add two boxes, your operator+() should only add two boxes.

The expression you want to use involves two operations, addition and multiplication. So you need to implement a multiplication operator overload. After that, you just let the order of operations take over. Because you want one operand to be a double or some other number type, it can't be a member function (the first operand would always be the calling object). Here I've implemented a possible solution.

#include <iostream>

class Box {
public:
Box() = default;
Box(double len, double bread, double h)
: length(len), breadth(bread), height(h) {}
double getVolume(void) { return length * breadth * height; }
void setLength(double len) { length = len; }
void setBreadth(double bre) { breadth = bre; }
void setHeight(double hei) { height = hei; }

Box& operator+=(const Box& other) {
this->length += other.length;
this->breadth += other.breadth;
this->height += other.height;

return *this;
}

friend Box operator+(Box lhs, const Box& rhs) {
lhs += rhs;

return lhs;
}

friend Box operator*(double z, const Box& box) {
return Box(z * box.length, z * box.breadth, z * box.height);
}

friend Box operator*(const Box& box, double z) {
return z * box;
}

private:
double length = 0.0; // Length of a box
double breadth = 0.0; // Breadth of a box
double height = 0.0; // Height of a box
};

// Main function for the program
int main() {
Box Box1(6.0, 7.0, 5.0); // Declare Box1 of type Box
Box Box2(12.0, 13.0, 10.0); // Declare Box2 of type Box
Box Box3; // Declare Box3 of type Box
double volume = 0.0; // Store the volume of a box here

// volume of box 1
volume = Box1.getVolume();
std::cout << "Volume of Box1 : " << volume << '\n';

// volume of box 2
volume = Box2.getVolume();
std::cout << "Volume of Box2 : " << volume << '\n';

// Add two object as follows:
Box3 = Box1 + 1.2 * Box2; // Changed to demonstrate new operator

// volume of box 3
volume = Box3.getVolume();
std::cout << "Volume of Box3 : " << volume << '\n';

return 0;
}

I made some other changes to the code as well. I added constructors to simplify initialization. I changed the way operator+() works to better align with the recommendations in the standard (link).

The multiplication operator doesn't fit under that umbrella, since your scaling factor cannot (and should not) be implicitly convertible to a Box. It is implemented as a pair of functions to preserve the commutative property of multiplication.

Overloading operator - multiple parameters

cout << 1 << 2 << 3 << 4;

This way this works is as a series of calls with two arguments e.g.

(((cout << 1) << 2) << 3) << 4;

Which is roughly equivalent to this:

cout << 1;
cout << 2;
cout << 3;
cout << 4;

So you don't write an operator<< taking multiple parameters, it always takes two operands, the left operand and the right operand. The left operand in the example above is cout i.e. the ostream and the right operand is the int being written to it. The operator returns the left operand, allowing it to be used again in the next operation, and so on for as many << operations as you chain together.

So cout << 1 returns cout again, so that (cout << 1) << 2 invokes the operator to write 1 to the stream and return the stream, then invokes the operator again on the return value to write 2 to the stream and then returns the stream again.

This is just nonsense:

friend int operator<<(const int &x, const int &y)
{
data += x;
data += y;
}

Where is data supposed to come from? A friend function is not a member of the class, so there is no this pointer, so no this->data, also you claim to return int but don't return anything, and this class is completely unrelated to Test. What you've written is an operator<< for two ints i.e. for doing 1 << 2 but that operator already exists, it's the bitshift operator, and you can't overload it for built-in types like int.

You want:

class Test
{
private:
int data;

public:
Test() { data = 0; }
void print() { cout << data; }

Test& operator<<(int y)
{
data += x;
return *this;
}
};

Or as a friend:

class Test
{
private:
int data;

public:
Test() { data = 0; }
void print() { cout << data; }

friend Test& operator<<(Test& t, int y)
{
t.data += x;
return t;
}
};

C++ [] array operator with multiple arguments?

Prior to C++23, you could not overload operator[] to accept multiple arguments. As a workaround, you instead can overload operator(). (See How do I create a subscript operator for a Matrix class? from the C++ FAQ.)


From C++23, as mentioned in a (deleted) answer by cigien, multiple subscript arguments can be passed to operator[] directly. See this demo from the cppreference page.

Why do I have to use long double for user defined literals?

C++11 draft n3290 has this to say about the parameters that user-defined literals can take (§13.5.8):

The declaration of a literal operator shall have a parameter-declaration-clause equivalent to one of the following:

const char*
unsigned long long int
long double
char
wchar_t
char16_t
char32_t
const char*, std::size_t
const wchar_t*, std::size_t
const char16_t*, std::size_t
const char32_t*, std::size_t

As you can see, double is not in that list, only long double is. So you have to use that for user-defined literals that expect a floating point number as an argument.



Related Topics



Leave a reply



Submit