Why does min still complain inside constexpr? - c++

I have the following code snippet:
#include <iostream>
#include <type_traits>
#include <algorithm>
#include <cstdint>
using T = double;
int main()
{
f();
}
void f() {
T x = 2;
if constexpr(std::is_integral_v<T>)
{
std::cout << std::min(static_cast<int64_t>(2), x);
} else {
std::cout << std::min(1.0, x);
}
}
The compiler is explaining that
<source>:15:57: error: no matching function for call to 'min(int64_t, T&)'
I thought it wouldn't be a problem because when T is a double, the first branch won't be instantiated. Apparently my understanding is wrong. Could someone help point out where my understanding goes wrong?

You need to make f() template, and T template parameter.
template <typename T>
void f() {
T x = 2;
if constexpr(std::is_integral_v<T>)
{
std::cout << std::min(static_cast<int64_t>(2), x);
} else {
std::cout << std::min(1.0, x);
}
}
then
int main()
{
f<double>();
}
For constexpr if:
(emphasis mine)
If a constexpr if statement appears inside a templated entity, and if
condition is not value-dependent after instantiation, the discarded
statement is not instantiated when the enclosing template is
instantiated .
Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive:
void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}

Outside of a template, the "false" branch of a constexpr if clause is discarded, not ignored. Thus, the code in such a branch must still be well-formed (and yours isn't, for the reason given).
From cppreference:
Outside a template, a discarded statement is fully checked. if constexpr
is not a substitute for the #if preprocessing directive.

std::min works with references. And both the arguments should be the same type. Since you're providing 2 different types, it cannot decide which one the argument type should be. You can work around that by explicitly specifying the type you like both the arguments to be converted:
std::min<double>(static_cast<int64_t>(2), x)
Be careful with dangling references.
The case when the failing branch of if constexpr doesn't matter is only in templates and f is not a template function.

Related

inline functions and forward references

At https://www.studytonight.com/cpp/inline-functions.php they are explaining inline functions
All the inline functions are evaluated by the compiler, at the end of class declaration.
class ForwardReference
{
int i;
public:
// call to undeclared function
int f()
{
return g()+10;
}
int g()
{
return i;
}
};
int main()
{
ForwardReference fr;
fr.f();
}
At the end they say :
"You must be thinking that this will lead to compile time error, but in this case it will work, because no inline function in a class is evaluated until the closing braces of class declaration."
......
why should one expect a compile time error? is it because no value for i has been set? If so, could someone explain better why it works, I don't get what's the point here, if the member functions are inline or not wouldn't it work the same?
The book is explicitly pointing out to you "no inline function in a class is evaluated until the closing braces of class declaration". Outside of a class declaration, with ordinary functions, this fails:
int f()
{
return g()+10;
}
int g()
{
return 0;
}
Try to compile a C++ source file containing just this, and nothing more, and see for yourself.
In C++, all functions must be declared before use. Since g() is not declared, this is ill-formed and your compiler will reject it. g() must be declared before it's referenced:
int g();
int f()
{
return g()+10;
}
int g()
{
return 0;
}
This does not applly to inline class method because, as your book explains, they are effectively evaluated when the class declaration is complete. At that point both f() and g() methods are declared, and the reference from f() to g() is well-formed.
What they are hinting at here, for why someone might expect a compile error, is the fact that although g is written below/after f, f can still reference it with no error.
If you were to move them out of the class and define+declare them at the same time then you would see the compile error (since f cannot see any g).
i.e.
int f(){
return g() + 10;
}
int g(){
return 32;
}
int main(){
return f();
}
See for yourself on compiler explorer.

is_constant_evaluated() should produce constexpr variables?

I have read the std::is_constant_evaluated() definition, but I still not sure why (1) is not working with the latest GCC: error: 'x' is not a constant expression
template<auto v>
struct s
{};
constexpr void f(int x)
{
if (std::is_constant_evaluated())
{
// constexpr int z=x; (1)
// s<x> a; (2)
}
}
int main(int argc, char* argv[])
{
f(4);
//f(argc);
return 0;
}
By the standard, should that work?
Or just the GCC implementation is buggy?
Somehow can I achieve the expected behavior? Which is basically:
With branching on std::is_constant_evaluated()
if it is true: the code can use variables as constexpr (like (2))
if it is false: the code use variables as non-constexpr
UPDATE
Can I 'transport' the constexpr-essiveness information into a function? Basically to decide in f() that it was call with constexpr x or not.
UPDATE
A more complex example about what I would like to achieve: this sample should stringify the parameter in compile time if possible.
template<auto v>
struct dummy_stringify
{
static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};
constexpr void f(int x)
{
if (std::is_constant_evaluated())
{
std::puts("A compile time calculation:");
//std::puts(dummy_stringify<x>::str);
} else
{
std::cout<<"A runtime calculation:"<<std::endl;
std::cout<<x<<std::endl;
}
}
int main(int argc, char* argv[])
{
f(4);
f(argc);
return 0;
}
x is not a constant expression, no matter how f itself is evaluated. That's a regular if right there (how is_constant_evaluated is meant to be used). It's not a discarded branch, so it has to contain well-formed code even when f is not constant evaluated. When x won't be a constant expression the function will still contain that (unexecuted) branch, and it will attempt to use x where a constant expression is required. That's plain ill-formed.
GCC is very much correct not to accept it.
The fundamental issue here is that, even with a constexpr (or even consteval) function being called during constant evaluation (and under an is_constant_evaluated check), there is still only one function shared among all argument values. You therefore can’t ever use a function parameter as a constant expression (even if the call with that parameter is a constant expression). If you want a constant-expression parameter, it has to be a template parameter.
UPDATE
I have found a solution with a little helper class (of course one can use std::integral_constant)
template<auto val_>
struct val
{
static constexpr auto value=val_;
};
template<auto v>
struct dummy_stringify
{
static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};
#include <iostream>
using namespace std;
template<class T>
constexpr void f(T x)
{
if constexpr(requires{ T::value; })
{
std::puts("A compile time calculation:");
std::puts(dummy_stringify<T::value>::str);
} else
{
std::cout<<"A runtime calculation:"<<std::endl;
std::cout<<x<<std::endl;
}
}
int main(int argc, char* argv[])
{
f(val<4>{});
f(argc);
return 0;
}
It can be improved with a template<char...> auto operator""_v(); to f(4_v)

compile error about template deduction on c++

#include <iostream>
template <int N>
class X {
public:
using I = int;
void f(I i) {
std::cout << "i: " << i << std::endl;
}
};
template <int N>
void fppm(void (X<N>::*p)(typename X<N>::I)) {
p(0);
}
int main() {
fppm(&X<33>::f);
return 0;
}
I just don't understand the compile error message of the code.
error: called object type 'void (X<33>::*)(typename X<33>::I)' is not a function or function pointer
p(0);
I think p is a function which returns void and takes int as its argument. But apparently, it's not. Could somebody give me clue?
Since p is a pointer to a nonstatic member function, you need an instance to call it with. Thus, first instantiate an object of X<33> in main:
int main() {
X<33> x;
fppm(x, &X<33>::f); // <-- Signature changed below to accept an instance
Then in your function, change the code to accept an instance of X<N> and call the member function for it:
template <int N>
void fppm(X<N> instance, void (X<N>::*p)(typename X<N>::I)) {
(instance.*p)(0);
}
The syntax may look ugly but the low precedence of the pointer to member operator requires the need for the parentheses.
As denoted in the comments already, p is a pointer to member function, but you call it like a static function (p(0);). You need a concrete object to call p on:
X<N> x;
(x.*p)(0);
// or:
X<N>* xx = new X<N>();
(xx->*p)(0);
delete xx;
Be aware that the .*/->* operators have lower precedence than the function call operator, thus you need the parentheses.
Side note: Above is for better illustration, modern C++ might use auto keyword and smart pointers instead, which could look like this:
auto x = std::make_unique<X<N>>();
(x.get()->*p)(0);

Passing a math function as method's input parameter C++

I am trying to write a general integral function and I would like to implement it in such a way so that it can accept any mathematical function. That is, I would like to pass the math function as an input parameter. In pseudo-code: simpson_int(x*x). I've heard of the function template in <functional> but I don't really have experience with templates in C++.
There are some solutions that comes in my mind (and this is my approach at the problem, for sure there are more solution, and maybe what I'm pointing out is not the best), that consider the fact that you need to call the argument function more than once in the Simpson implementation (thus you need a "callable" argument):
Function pointer
Function pointers (more C than C++), where you declare with two arguments: the first one will be the pointer to a function with the specified types, while the second is the argument for your function. Lets
make an example:
#include <iostream>
double power2(double x) {
return x * x;
}
double simspon(double (*f)(double), double x) {
return f(x);
}
int main() {
std::cout << simspon(power2, 2);
return 0;
}
In this case I have used no templates for reaching the result. But this will not take any function as first argument, but only a function that has as argument a double and returns a double.
I think that most of c++ programmer will suggest you to avoid this method.
Function pointer and templates
So you maybe want to expand the previous example using templates and making it more general. It is quite simple to redefine the function
to accept a template (an abstract type) that you actually specify only when you use it in your code:
#include <iostream>
double power2(double x) {
return x * x;
}
int power2int(int x) {
return x * x;
}
template <class T, class P>
P simspon(T (*f)(P), P x) {
return f(x);
}
int main() {
std::cout << simspon<double, double>(power2, 2.0);
std::cout << simspon<int, int>(power2int, 2);
return 0;
}
T and P are two templates: the first one is used for describing the returned value of the function pointer, while the second specify the argument of the function pointer, and the returned value of simpson.So when you are writing template <class T, classP> you are actually informing the compiler that that you are using T and P as placeholder for different type. You will actually declare the type that you want later on, when you will call the function in the main. This is not good code but I'm building the path to understand templates. Also, you specify the type of your argument function when you actually call simpson, with the < >.
(Disclaimer: you should consider to use template <typename T ...> instead of class. But I'm used with the old class and there are situation in which typename cannot be used, there are a lot of questions on SO that dive into this.)
Using std::function
Instead of using a function pointer as argument you may want to create a variable that stores your function to be passed as argument of simpson. This bring several advantages, because they are actually an object inside your code that have some predictable behavior in some unwanted circumstances (for example, in case of a null function pointer you have to check the pointer itself and handle it, in case of std::function if there is no callable pointer it throws std::bad_function_call error)
Here an example, and it uses again templates, as before:
#include <iostream>
#include <functional>
double power2(double x) {
return x * x;
}
int power2int(int x) {
return x * x;
}
template <class T, class P>
P simspon(std::function<T(P)> f, P x) {
return f(x);
}
int main() {
std::function<double(double)> p_power2 = power2;
std::cout << simspon<double, double>(p_power2, 2.0);
std::function<double(double)> p_power2int = power2int;
std::cout << simspon<int, int>(power2int, 2);
return 0;
}
Using lambdas
lambdas are closure and in your case (if you can use the standard C++14) can be used alongside the auto keyword to achieve quite a general behavior, without the explicit use of templates. The closure are also able to capture part/the whole context, check the reference for this.
Let's see an example, in which I create two lambdas that receive different arguments and a simpson function that is quite general (actually it is not, is the compiler that defines different functions with respect to the call that you do).
#include <iostream>
auto lambda = [](auto x) { return x * x ; };
auto lambda_2 = [] (int x) { return x + 10; };
auto simpson(auto f, auto x) {
return f(x);
}
int main() {
std::cout << simpson(lambda, 2.0);
std::cout << simpson(lambda_2, 1);
return 0;
}
You need to compile it with the -std=c++14 flag. There are tons of advise that comes in my mind to suggest you to avoid to implement your code in this way, remember that it has only some illustrative purposes (I've more than exaggerated with the auto keyword).
Function objects (the Problem class)
Maybe an improvement for your case is to write a general class for the mathematical functions to integrate and pass the object to your function. This bring several advantages: you may want to save some of the integrative result inside your function or even write the stream operator to pretty print your problem. This is the solution employed typically by mathematical libraries.
In this extremely simple case, we have a class that is a problem. When you create a new instance for this class, a std::function is passed to the constructor and stored inside the class. The instance of the class is the argument for your simpson:
#include <iostream>
#include <functional>
template <class T, class P>
class Problem {
public:
// Attributes
std::function<T(P)> _f;
// Constructor
Problem(std::function<T(P)> f) : _f(f) {};
// Making the object callable
P operator()(P x) { return _f(x); }
};
template <class T, class P>
P simspon(Problem<T, P> p, P x) {
return p(x);
}
int main() {
Problem<double, double> prb([](double x) { return x * x; });
std::cout << simspon<double, double>(prb, 2);
return 0;
}
Use std::function, like this for example:
#include <iostream> // std::cout
#include <functional> // std::function
int main()
{
std::function<double(double)> simpson_int =([](double x) { return x * x; };
std::cout << "simpson_int(4): " << simpson_int(4) << '\n';
return 0;
}
which outputs:
simpson_int(4): 16

Why doesn't the Standard allow the definition of the two functions `f` below?

§14.8.2/4 allows the instantiation of two different functions, g<int> and g<const int> from the template definition. Why doesn't the Standard allow the definition of the two functions f in the code below? I know that both functions would have the same type void(int). But that also happens with the instantiated functions g. The note in §14.8.2/4 says: f<int>(1) and f<const int>(1) call distinct functions even though both of the functions called have the same function type..
#include <iostream>
template<typename T>
void g(T t) { std::cout << t << '\n'; }
void f(int i) { std::cout << i << '\n'; }
//void f(int const i) { std::cout << i << '\n'; } // doesn't compile
int main()
{
g<int>(1);
g<int const>(2);
}
Top-level consts on the parameter types are not part of the function signature. So the two versions of f() you've defined are the same function as far as overload resolution is concerned making the second one a redefinition.
From §13.1/3 [over.load]
— Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called. [ Example:
typedef const int cInt;
int f (int);
int f (const int); // redeclaration of f(int)
int f (int) { /* ... */ } // definition of f(int)
int f (cInt) { /* ... */ } // error: redefinition of f(int)
—end example ]
Only the const and volatile type-specifiers at the outermost level of the parameter type specification are ignored in this fashion; const and volatile type-specifiers buried within a parameter type specification are significant and can be used to distinguish overloaded function declarations.
The fact that top-level const is not part of the function signature allows for a minor advantage.
Suppose you have a function"
void f(int);
in its implementation, if you know you are not going to change the input parameter, you can declare:
void f(int const x) {
std::cout << x << "\n";
}
and this is none of the business of the caller. Later, it turns out it would be useful to munge the input value (say, you want to treat negative integers as 0):
void f(int x) {
if (x<0) x = 0;
std::cout << x << "\n";
}
and without changing the signature or the rest of the body of the function, we are good to go.
Basically, the top level constness of arguments doesn't impact the usual binary calling conventions of C++, and logically the constness is no business of the caller. By eliminating that from the signature, we get some benefit.
For template functions, however, the types impact both the signature and the body of the function, and that body is part of the template's interface. (decltype lets the types of function parameters impact the body, but the body is not part of the interface like a template)