constexpr, or not constexpr, that is the question
A function that's constexpr
means that the function potentially can be evaluated at compile-time. The function must however also be callable with a run-time value, and produce a run-time result.
A variable that's constexpr
has to be a compile time constant. Period.
What that means for your validateIP
function is that you don't need to make count
constexpr
. You can write a function and mark it as constexpr
.
When you call the function with a compile time constant, it will get evaluated at compile time.
If you call it with a run time value it will get evaluated at run time.
#include <string_view>
#include <iostream>
constexpr bool validateIP(const std::string_view& ip) {
int count = 0;
for (auto& c : ip) {
if (c == '.') {
++count;
}
}
return count == 3;
}
int main()
{
// Assigning the value to a constexpr is not needed to make the function
// be evaluated at compile time, but it proves that it is in this case
constexpr auto isValid1 = validateIP("123.456.789.0");
std::cout << isValid1;
}
std::abs can be used in constexpr function, but only if it's templated. Why?
For a regular function the compiler may know, based on the type of the function parameters, if the inner code can be potentially evaluated in compile time. This is why you get an error for calling std::abs
in MSVC and clang. The behavior of gcc is based on its decision to implement std::abs
as constexpr
which is by the way a questionable decision.
For a template function the compiler cannot know if the inner code can be evaluated in compile time, as it may be based on the actual type of the template arguments, with different functions overload being called. While most compilers would decide not to check whether all possible overloads of std::abs
cannot be constexpr
, thus letting the code pass compilation, theoretically a compiler may check (in very specific cases that can be checked, like this one) and since the user is not allowed to extend std
by adding a new version of abs
(the list of allowed extensions to std
is closed by the spec) it is possible to see that the function can never be constexpr
and thus to generate a compilation error. In the more general case however, the compiler cannot check for a template function if all possible cases cannot produce a constexpr
function, since it sees only the available overloads for the inner call, per each call to the template function, and there might be other available overloads for the inner call, when the template is called elsewhere.
Note that making a constexpr
function a template, just so it can get compiled, would not be a good approach. The actual decision if the function is constexpr
(i.e. can be called in compile time) would be based on the actual call, and if in all cases the function cannot be constexpr
you are trying in a way to cheat the compiler but eventually are cheating mainly yourself...
By the way, in my check with clang 10.1 and trunk versions, I don't get compilation error on the template version, this code compiles both with gcc and clang:
template<typename T>
constexpr T myabs(T t) {
return std::abs(t);
}
int main() {
int i = myabs(3);
}
While this compiles with gcc (which implements std::abs
as constexpr
) and fails with clang:
int main() {
constexpr int i = myabs(3);
}
It seems that both gcc and clang do not generate an error even if the inner call inside a constexpr
template function is not dependent on the template parameters and can never be a constant expression:
int myabs() {
return 42;
}
template<class T>
constexpr int f() {
// this is never a contexpr
// yet gcc and clang are ok with it
return myabs();
}
And again, this is allowed as no diagnostic is required for non-conforming constexpr
template functions:
[dcl.constexpr] 9.2.5/7 - The constexpr and consteval specifiers:
[...] If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required.
Constexpr compile error using std::acos with clang++ not g++
Clang is correct here, we are not allowed to use acos
in a constant expression.
The issue is that acos is not marked constexpr in the standard but gcc treats some functions not marked in the standard including acos as constexpr. This is a non-conforming extension and should eventually be fixed in gcc.
Builtin functions are often used to constant fold and we can see if we use -fno-builtin
with gcc it disables this non-conforming behavior and we will receive the following error:
error: call to non-constexpr function 'double acos(double)'
constexpr T pi{std::acos(T(-1.0))};
^
Why doesn't the C++ standard library provide constexpr versions of the cmath functions?
It hasn't been standardized yet. An initial proposal was submitted just last week, but only covering utility and linear operations and not any transcendental functions. Math is hard and floating-point math is complicated. For example, implementations don't allow overflows to infinity in constexpr
, but this isn't yet clearly standardized.
The compiler's constexpr
interpreter would have to special-case the math library interface, since unlike the rest of the standard library, it can't see its implementation.
GCC does offer constant evaluation of math functions as a nonconforming extension.
Work around disregarded constexpr in Visual Studio?
It looks like your project includes the standard std::log2
function which the compiler confuses with the your log2
function. This can happen even if you don't #include <cmath>
because standard headers are allowed to include any other standard headers. This is also another example of using namespace std;
backfiring.
One solution is to rename your constexpr
function to something else :
#include <bitset>
#include <iostream>
using namespace std;
constexpr int logTwo(const unsigned int x) {
return x < 4 ? 1 : 1 + logTwo(x / 2);
}
int main() {
bitset<logTwo(2)> foo;
int bar[logTwo(8)];
cout << logTwo(8) << endl;
}
Demo
Edit : It seems that using namespace std;
may be unrelated in this case. The standard log2
function may be available at the global namespace anyway.
Getting constexpr to work with pow in C++17 on OSX
First, you can't initialize constexpr
class members with functions that aren't constexpr
and std::pow
isn't constepxr
in standard C++17 . The workaround is to declare them const
. While they can't be used in places that require a compile time const
they are immutable. A traditional approach is declaring them in header which you include as needed in source files. Then you hneed one implementation file that defines the static const members.
If your code requires compile time const's or constexpr your only option is to write your own pow
.
Here's one way to initialize const statics with functions that are not constexpr
prior to executing the main() using a portion of your question that shows the technique:
Create a header, constinit.h, that declares the class
// include header guards
// declare the static consts
struct ClassName {
static double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1; // represents 0.99
static double DEFAULT_TARGET_INITIAL_PBAD; // to be initialized by pow
};
Create an implementation file that initializes the statics:
#include "constinit.h"
#include <cmath>
double ClassName::DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1{ 2 }; // represents 0.99
double ClassName::DEFAULT_TARGET_INITIAL_PBAD = (1 - std::pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
To use the statics:
#include <iostream>
#include "constinit.h"
int main()
{
std::cout << ClassName::DEFAULT_TARGET_INITIAL_PBAD << std::endl;
}
If constexpr
for compile time initialization is required you need to define your own constexpr
pow function. This works in C++17:
#pragma once // or header guards per your preference
constexpr double my_pow(double x, int exp)
{
int sign = 1;
if (exp < 0)
{
sign = -1;
exp = -exp;
}
if (exp == 0)
return x < 0 ? -1.0 : 1.0;
double ret = x;
while (--exp)
ret *= x;
return sign > 0 ? ret : 1.0/ret;
}
class ClassName {
public:
static constexpr double DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1 = 2; // represents 0.99
static constexpr double DEFAULT_TARGET_TFINAL_DIGITS_FROM_0 = 10; // represents 1e-10
static constexpr double DEFAULT_TARGET_INITIAL_PBAD = (1 - my_pow(10, -DEFAULT_TARGET_TINITIAL_DIGITS_FROM_1));
static constexpr double DEFAULT_TARGET_FINAL_PBAD = my_pow(10, -DEFAULT_TARGET_TFINAL_DIGITS_FROM_0);
static constexpr double DEFAULT_ERROR_TOL_DIGITS = 0.9; // as a fraction of digits in the last place from the above.
static constexpr double DEFAULT_SAMPLE_TIME = 1;
// more unrelated code
};
Related Topics
Why Vector≪Bool≫::Reference Doesn't Return Reference to Bool
Create N-Element Constexpr Array in C++11
Libpng Warning: Iccp: Known Incorrect Srgb Profile
How Is Std::Function Implemented
How Do C++ Class Members Get Initialized If I Don't Do It Explicitly
Export All Symbols When Creating a Dll
Sin and Cos Give Unexpected Results For Well-Known Angles
Advantages of Auto in Template Parameters in C++17
How to Find If a Given Key Exists in a C++ Std::Map
Efficiently Load a Large Mat into Memory in Opencv
How to Convert an Opencv Cv::Mat to Qimage
How to Calculate Execution Time of a Code Snippet in C++
Py_Initialize Fails - Unable to Load the File System Codec
How to Read a File Line by Line or a Whole Text File At Once
Difference Between String and Char[] Types in C++
C/C++: Switch For Non-Integers
Why Is This Program Erroneously Rejected by Three C++ Compilers