Agument to constexpr functions

Pages: 12
Hello, I'm new into programming and I started to learn about it using the book : Programming principles and practice using C++. Today I'm here becaue I have some problems in understanding constexpr functions. In chapter 8 the author introduces them in some lines and a brief example using these words :


A function represents a calculation, and sometimes we want to do a calculation at compile time. The reason to want a calculation evaluated by the compiler is usually to avoid having the same calculation done millions of times at run time.

We convey our intend to have a function evaluated at compile time by declaring the fucntion as a constexpr function. A constepxr function can be evaluated at compile time just if it is given constant expressions as arguments.


1
2
3
4
constexpr double xscale = 10; // scaling factors 
constexpr double yscale = 0.8; 

constexpr Point scale(Point p) { return { xscale*p.x, yscale*p.y }; };


Assume that point is a simple struct with members x and y representing 2D coordinates. Now, when we give scale() a Point argument, it returns a point with coordinates scaled according to the factors xscale and yscale. For example:
1
2
3
4
5
6
7
8
9
10
void user(Point p1)

Point p2{10,10}; 

Point p3 = scale(p1); 
Point p4 = scale(p2) // p4 == {100,8}

constexpr Point p5 = scale(p1); // error : scale is not a constant expression
constexpr Point p6 = scale(p2); // p6 == {100,8}; 


And here the are my questions :
* Why p2 is considered a constant expression ? I knew that a variable, in this case the p2 data members, cannot be considered a constant expression. Could you please explain me why p2 is considered a constant expression ?


* Can you give me a simple and useful example where I could need to use a constexpr function ?

Last edited on
> Why p2 is considered a constant expression ?

p2 is not a constant expression.
For it to be usable as a constant expression, we need to write: constexpr Point p2{10,10};


> Can you give me a simple and useful example where I could need to use a constexpr function ?

There are situations in C++ where a constexpr is required: for instance, the size of an array, or a template non-type argument.
bool a[ std::numeric_limits<unsigned short>::max() ] {} ;
@JLBorger Ok. I understand what you're saying, but I still can't understand a thing : the 2 data members of point needs to be constant to be passed to the function scale() ? I don't have the definition of point so I can't see by myself wether they are constant data members or not. Could we pass them to the scale function if they were "simple" data members such as 2 double ?
> I don't have the definition of point so I can't see by myself wether they are constant data members or not.

1
2
// Assume that point is a simple struct with members x and y representing 2D coordinates.
struct Point { double x ; double y ; } ;


> the 2 data members of point needs to be constant to be passed to the function scale() ?
> Could we pass them to the scale function if they were "simple" data members such as 2 double ?

They need not be constant expressions.
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
#include <iostream>

// Assume that point is a simple struct with members x and y representing 2D coordinates.
struct Point { double x ; double y ; } ;

constexpr double xscale = 10; // scaling factors
constexpr double yscale = 0.8;

constexpr Point scale(Point p) { return { xscale*p.x, yscale*p.y }; }

int main()
{
    constexpr Point p1 { 1.2, 3.4 } ; // constexpr

    double a, b ;
    std::cin >> a >> b ;
    const Point p2 { a, b } ; // can't be a constant expression

    constexpr Point p3 = scale(p1) ; // constexpr

    Point p4 = scale(p2) ; // can't be a constant expression

    constexpr Point p5 { p1.x / 2, p3.y * 4 } ; // constexpr

    const Point p6 { p1.x / a, p3.y * b } ; // can't be a constant expression

    constexpr Point p7 = scale( scale( scale(p1) ) ) ; // constexpr
}

http://coliru.stacked-crooked.com/a/e7c3235b30ae5758
@JLBorges I've got some problem with your code due to a compiler error : p3 illegal initialization of a constexpr entity with a non-constant expression. I also have a warning message about not supported class literal types. Maybe I should update visual studio 2013 ?
> Maybe I should update visual studio 2013 ?

Yes. To Visual Studio 2015 (either Community or Desktop).

This may not work in Visual Studio 2015 either, but you should upgrade anyway.

A note about C++11 constexpr: this is a large feature with complicated interactions throughout the Core Language, implemented in a compiler codebase undergoing modernization, being shipped for the first time. It contains bugs/limitations, some of which you might notice, and many of which will be fixed in Updates. ...

- STL in http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx
closed account (E0p9LyTq)
Updating to VS 2015 won't help. It chokes on constexpr functions too.

constexpr double e = exp(1);

compiles just fine with TDM-GCC 4.9.2, but VS 2015 Community errors.

Error	C2131	expression did not evaluate to a constant	ConsoleApplication1	c:\users\public\documents\my projects\consoleapplication1\consoleapplication1\consoleapplication1.cpp	7
@JLBorges I got some problem if I try to initialize point members's x and y to zero. The compiler segnale an error about constant expression that doesn't return a costant.
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
struct point_one 
{ 
    double x = 0 ; 
    double y = 0 ; 
} ;

struct point_two 
{ 
    constexpr point_two() : x(0), y(0) {} // constexpr
    constexpr point_two( double x, double y ) : x(x), y(y) {}  // constexpr

    double x ; 
    double y ; 
} ;

int main()
{
    constexpr point_one one {} ;
    constexpr point_one one_a { 0, 0 } ;
    constexpr point_one one_b { 10, 20 } ;
    
    constexpr point_two two {} ;
    constexpr point_two two_a { 0, 0 } ;
    constexpr point_two two_b { 10, 20 } ;
}

http://coliru.stacked-crooked.com/a/058b61066e4ee823
FurryGuy wrote:
Updating to VS 2015 won't help. It chokes on constexpr functions too.
constexpr double e = exp(1);
compiles just fine with TDM-GCC 4.9.2, but VS 2015 Community errors.

gcc 4.9 is wrong: std::exp is not a constexpr function and the standard forbids turning it into constexpr.

clang will tell you:
1
2
3
4
5
6
ain.cpp:3:18: error: constexpr variable 'e' must be initialized by a constant expression
constexpr double e = exp(1);
                 ^   ~~~~~~
main.cpp:3:22: note: non-constexpr function 'exp' cannot be used in a constant expression
constexpr double e = exp(1);
                     ^


The standard actually went back and forth on this initially and gcc went ahead with an earlier allowance to make whatever constexpr.. They may yet catch up: https://gcc.gnu.org/ml/libstdc++/2014-09/msg00004.html
Last edited on
Does the same apply to other cmath functions?
closed account (E0p9LyTq)
@Cubbi

http://coliru.stacked-crooked.com/a/34d1502ca546cd1c

GCC 5.2.0 doesn't seem to have a problem with constexpr double e = exp(1);
Last edited on
yes, it applies to all of cmath and yes it's still broken in 5.2 (where "broken" means "proprietary non-portable extension is enabled in strict standard compliance mode")
Last edited on
That's a shame. What's the rationale behind that decision?
closed account (E0p9LyTq)
yes, it applies to all of cmath and yes it's still broken in 5.2 (where "broken" means "proprietary non-portable extension is enabled in strict standard compliance mode")

Now that is something I can understand. It is hard to "get a handle on" the new features when the very tools one uses to learn is non-standard.

Personally I might disagree with that rationale, but I am not on the ISO committee. I have to follow the rules they set up.
Last edited on
helios wrote:
What's the rationale behind that decision?

http://open-std.org/JTC1/SC22/WG21/docs/lwg-defects.html#2013 says "users may use SFINAE to observe different behavior from otherwise identical code."
"users may use SFINAE to observe different behavior from otherwise identical code."


Can I get an example? I'm not sure how that would happen.
A trivialised example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <type_traits>
#include <cmath>

template < typename T >
typename std::enable_if< bool( std::exp( T() ) > 0 ), bool >::type foo(T) { std::cout << "foo(int)" ; return false ; }

bool foo( double ) { std::cout << "foo(double)" ; return true ; }

int main()
{
    if( foo(7) ) std::cout << " - this is a conforming implementation\n" ;
    else std::cout << " - this implementation is broken\n" ;
}

uname -a
echo && clang++ --version | grep clang && clang++ -std=c++14 -stdlib=libc++ -O3 -Wall -Wextra -pedantic-errors main.cpp && ./a.out 
echo && g++ --version | grep GCC && g++ -std=c++14 -O3 -Wall -Wextra -pedantic-errors main.cpp  && ./a.out
Linux stacked-crooked 3.2.0-74-virtual #109-Ubuntu SMP Tue Dec 9 17:04:48 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

clang version 3.6.0 (tags/RELEASE_360/final 235480)
foo(double) - this is a conforming implementation

g++ (GCC) 5.2.0
foo(int) - this implementation is broken

http://coliru.stacked-crooked.com/a/1e66efbade66d8cc
But why not make these functions constexpr?
Essentially for the same reason that template non-type parameters can't be floating point values.
Pages: 12