user defined literal, REQUIRES long double???

QUESTION 1:
Why do I get a compile error invalid parameter list when I set the user defined literal a double in the parameter list, instead of a long double? Is it because these literals are designed for scientific notation & they expect there to be much bigger numbers at times & just force you to use long double? I never would have figured this out if I had to do this on my own & with the given compile error!

So that I cannot set it like this:
Temperature operator "" _F (double fahrenheit)

QUESTION 2:
I think I understand the order of operation/creation, is this it....

1) "Temperature k1 = 31.73_F;" first operator ""_F is called & makes it's own temporary Temperature object, sets its Kelvin value, returns that pass by value to object k1 & then the temp object gets destroyed.

2) Object k1 then gets created with the passed by value.

Also, the author used structs instead of a class, but wouldn't it be more efficient to make a class instead and not need to create that temp object?

1
2
3
4
5
Temperature& operator ""(long double fahrenheit)
{
     this.Kelvin = ((fahrenheit + 459.67) * 5/9);
     return *this;
}


or maybe this way that is even more efficient & that does not need to update ALL of the variables if the Temperature class had many different variables?

1
2
3
4
void operator ""(long double fahrenheit)
{
     this.Kelvin = ((fahrenheit + 459.67) * 5/9);
}


QUESTION 3:
A follow up from the one right above. This is common syntax for prefix ++ operator:

1
2
3
4
5
6
7
8
9
10
Date& operator ++()
{
     ++day;
     return *this;
}
void DisplayDate()
{
	cout << month << "/" << day << "/" << year << endl;
}


But if I used the prefix ++ operator like this because it works and it is more efficient, will someone say something and is this a big NO, NO.....someone can say operator ++ is EXPECTED to return Date& ?????

1
2
3
4
5
6
7
8
void operator ++()
{
     ++day;
}
void DisplayDate()
{
	cout << month << "/" << day << "/" << year << endl;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 #include <iostream>
using namespace std;

struct Temperature
{
	double Kelvin;
	Temperature (long double kelvin): Kelvin(kelvin){}

};
	
Temperature operator "" _C (long double celcius)
{
	return Temperature(celcius + 273);
}
	
Temperature operator "" _F (long double fahrenheit)
{
	//return Temperature ((fahrenheit-32) * 5/9 + 273);
	return Temperature ((fahrenheit + 459.67) * 5/9);
}

int main()
{
	Temperature k1 = 31.73_F;
	Temperature k2 = 0.0_C;
	
	cout << "k1 = " << k1.Kelvin << " Kelvin" << endl;
	cout << "k2 = " << k2.Kelvin << " Kelvin" << endl;

	return 0;
}
Last edited on
Why do I get a compile error invalid parameter list when I set the user defined literal a double in the parameter list, instead of a long double?


cppreference wrote:
2) For user-defined floating-point literals,
a) If the overload set includes a literal operator with the parameter type long double, the user-defined literal expression is treated as a function call operator "" X(fL), where f is the literal without ud-suffix
b) otherwise, the overload set must include either, but not both, a raw literal operator or a numeric literal operator template. If the overload set includes a raw literal operator, the user-defined literal expression is treated as a function call operator "" X("f")
c) otherwise, if the overload set includes a numeric literal operator template, the user-defined literal expression is treated as a function call operator "" X<'c1', 'c2', 'c3'..., 'ck'>(), where c1..ck are the individual characters of f.


Q2

Not sure why you think it is inefficient with a temporary object if it is only a double?

Also, the author used structs instead of a class, but wouldn't it be more efficient to make a class instead and not need to create that temp object?


class and struct are almost the same in c++, the only difference being the default access, if not specified. struct is public, class is private.

5/9 is zero, integer division.

Operators return an object of the type so that they can be used in expressions. One couldn't do lines 24 and 25 if it didn't.

Looks like the long double is required then, thanks. I am thinking in terms of game programming where these minutia calls & memory allocations add up significantly & every morsel counts to make a faster & more robust product.

With QUESTION 3, will anyone have a problem with the 2nd code snippet over the 1st for the prefix operator++?
Q3: Returning Date& is strongly preferred. Returning Date& allows an object to be used in an expression. Returning void does not allow the object to be used in an expression.
I am just curious, do you guys use these operators so much that you just remember which is the preferred way or do you need to reference them from time to time?

In the book operator + is coded like this, with a return:
1
2
3
4
5
6
7
8
9
10
11
	Date operator + (int daysToAdd)
	{
		Date newDate (month, day + daysToAdd, year);
		return newDate;
	}
	
	Date operator - (int daysToSub)
	{
		
		return Date(month, day - daysToSub, year);	
	}


Operator+ and operator+= pretty much do the same. BUT THEN in the book operator += is coded like this, with a VOID:

1
2
3
4
5
6
7
8
9
	void operator += (int daysToAdd)
	{
		day += daysToAdd;
	}

	void operator -= (int daysToSub)
	{ 
		day -= daysToSub;
	}


So naturally I tried the same code but with a Date return and just as I expected it to, it worked.

1
2
3
4
5
6
7
8
9
	Date operator += (int daysToAdd)	
	{
		return Date (month, day + daysToAdd, year);
	}
	
	Date operator -= (int daysToSub)	
	{
		return Date (month, day - daysToSub, year);
	}


So even in this last book example then, 2nd snippet Date return that I coded is still preferred over the void, right?
Last edited on
help the compiler do the least work under the hood.
returning a value may cause it to invoke an unnecessary bunch of work.. construction of a fat object temporary if nothing else, if say that date object constructor is heavy and the compiler decides it needs to happen. If you modify 'this' and return void/nothing, it can avoid that work. It may be able to avoid it anyway, but look at your code.. there is no way it would consider creating a new date in the void version...
So the answer then becomes, it is the preferred and most often seen way to return the value but there is freedom for when you do not need to return to main & can just update the object member directly and when resources are constrained....nobody is going to have a fit if we returned void.
do you guys use these operators so much that you just remember which is the preferred way or do you need to reference them from time to time?
Both.
The += operators are not coded as would be expected. The expectation is that they would return a type Date& (ref to *this). This is so that the result can be used. The same with all the variations of operator= (+=, -= etc).

You can, of course, code them so that they do whatever you code them to do and return what you want. However there is a 'convention' that user-provided operator overloads work in a similar manner to the provided ones and as 'might be expected' to those using them. One sure fire way to really annoy anyone trying to use is to have them behave differently to that expected.

1
2
3
4
5
Date& operator ++()
{
     ++day;
     return *this;
}


This is as would be expected for the interface specification. Same for +=, -= etc.

Note that for post inc/dec then you just return Date and not Date& as you can't then return a ref to *this as you re returning the value before the increment and not after. This involves a copy. This is why pre-inc/dec is always preferred toa post inc/dec when either is appropriate.
Looks like the long double is required then, thanks. I am thinking in terms of game programming where these minutia calls & memory allocations add up significantly & every morsel counts to make a faster & more robust product.

I like this viewpoint. Each decision should maximize the quality of the final product. Not "readability", not "code quality", but product quality. Still, you're overestimating costs. There should be no speed or memory footprint at all because the UDL should be executed by the compiler. You can encourage the compiler to do this by declaring it appropriately:

With C++11 you should declare this UDL constexpr as follows
1
2
constexpr Temperature operator""_C(long double celsius) 
{ return Temperature{static_cast<double>(celsius + 273.0l)}; }

With C++20, you should declare it consteval
1
2
consteval Temperature operator""_C(long double celsius) 
{ return Temperature{static_cast<double>(celsius + 273.0l)}; }


A cooked user-defined literal causes the compiler to turn the raw text of the literal into a number for you. The type of this value is long double, because it is the largest floating point type that the compiler can understand. The parsing is, in practice, done at compile time. But if for some reason long double is still not acceptable you can define a raw user-defined literal and do the parsing yourself, preferably in a way that ensures the source code is parsed by the compiler and not by the application during its own run-time.

If you're curious why UDLs are designed the way they are, read n2378 for design rationale:
https://wg21.link/n2378

There is no temporary object resulting from your calls to operator""_F and operator""_C.
https://en.cppreference.com/w/cpp/language/copy_elision
https://en.cppreference.com/w/cpp/language/copy_initialization
Last edited on
Arguably optimizing readability and code quality raises the ceiling of maximum product quality.
Topic archived. No new replies allowed.