Non-type template Arguments - c++

I was reading an article about non-type template arguments, and it said that :
When being instantiated, only compile time constant integer can be passed. This means 100, 100+99, 1<<3 etc are allowed, since they are compiled time constant expressions. Arguments, that involve function call, like abs(-120), are not allowed.
Example :
template<class T, int SIZE>
class Array{};
int main(){
Array<int, 100+99> my_array; // allowed
Array<int, abs(-120)> my_array; // not allowed
}
what's the difference between 100+99 and abs(-120) ?
how come 100+99 are compiled time and abs(-120) is not?

None, and abs(-120) is entirely legal in C++11. C++03, as you adequately point out, did not have scope for functions which could evaluate at compile-time, but C++11 does. For abs directly, you could replace it with a template which performs the same computation and use abs_template<-120>::value in C++03.
Edit: I meant to say that, even if abs was not constexpr, you could trivially write your own abs which is constexpr. Coulda sworn I edited that in.

100+99 is optimized out to 199 at compile time.
abs() is function and it may or may not be marked constexpr (C++11 feature, that would allow you to do so; you can easily check cppreference or standard to see if it's constexpr in C++11). It requires to be executed; compiler cannot deduce that it's state less function returning same value for every run with same argument.

Related

Why can't we use compile-time 'variables' in consteval functions as template parameters?

I was testing this code (https://godbolt.org/z/fe6hhbeqW)...
// Returns the nth type in a parameter pack of types (ommited for clarity)
// template <std::size_t N, typename...Ts>
// nth_type{}
template <typename... Ts>
struct Typelist{
template <typename T>
consteval static std::size_t pos() noexcept {
for(std::size_t i{}; i < sizeof...(Ts); ++i) {
using TN = nth_type_t<i, Ts...>;
if (std::is_same_v<T, TN>)
return i;
}
return sizeof...(Ts);
}
};
and I was puzzled for it not working. GCC and clang agree on i not being a constant expression, so they refuse to let me pass it as a template parameter. However, i is clearly known at compile-time so, to my limited understanding, the compiler shouldn't have any problem to use it to instantiate the template.
Is there a reason for this not to work? Will it work in the future? I have tested with trunk versions of both compilers, with same result.
It doesn't matter that i is guaranteed to be evaluated only at compile-time when its value is known in an abstract sense.
It also doesn't matter whether the function is consteval or constexpr or none of these.
The language is still statically typed and nth_type_t<i, Ts...>; must in any given instantiation of the function refer to exactly one type. If i can change in the for loop, that is not possible to guarantee.
The language requires that the expression i when used as template argument is by itself a constant expression, independently of whether the whole function body can only be evaluated as part of a larger constant expression. But i is neither declared constexpr, nor declared const with constant initializer.
While this is not possible currently like the other answer says, there is a proposal in the works that would make this kind of loop possible to write. (I am told it was even meant to be included in C++20, but for whatever reason, it was left out at the last moment.)
The proposal is P1306, currently named ‘Expansion Statements’, and with any luck it should be included in C++23. There is a tracking issue on GitHub; the committee proceedings, however, are only visible to members.

Are static constinit member variables identical to non-type template parameters?

I have a class template for an N-dimensional array:
template<typename T, std::size_t... Shape>
class ndarray { ... };
One consequence of this template design is that there is an additional 'implicit' template parameter if you will: std::size_t Size, the product of all arguments in Shape. I have been using a C++17 fold expression ((1 * ... * Shape)) where I would typically use Size if it weren't dependent on Shape, but I'm wondering if adding the following 'alias' would result in any subtle differences in the compiled assembly:
template<typename T, std::size_t... Shape>
class ndarray {
static constinit std::size_t Size = (1 * ... * Shape);
...
};
Interestingly, the ISO C++20 standard doesn't state whether or not constinit implies inline as it does for constexpr and consteval. I think the semantics of constinit (especially in relation to constexpr variables) only really makes sense if the variable were also inline, but its omission from the standard makes me wary of this conclusion.
constinit has exactly and only one semantic: the expression used to initialize the variable must be a constant expression.
That's it. That's all it does: it causes a compile error if the initializing expression isn't a valid constant expression. In every other way, such a variable is identical to not having constinit there. Indeed, the early versions of the proposal had constinit as an attribute rather than a keyword, since it didn't really do anything. It only gave a compile error for invalid initialization of the variable.
For this case, there is no reason not to make the variable constexpr. You clearly don't intend to change the variable, so there's no point in making the variable modifiable.
No, these are not at all the same because constinit doesn't make the variable const. As such, the initialization of Size isn't even valid. Writing constinit const is mostly equivalent to writing constexpr, which is semantically equivalent to the constant fold-expression. There's no guarantee that any compiler does treat them identically, but in most constant-expression cases there isn't much room for variation.
constinit also doesn't imply inline: it isn't that the standard "doesn't state whether", it's just that there's nothing to say. Since there's no point in putting constinit and constexpr on the same variable, it's not clear what semantics you expect in that area.

Would it be sufficient for constexpr, consteval, and constinit to be definitions instead of keywords?

It seems that the rules for the compile-time keywords: constexpr, consteval and constinit are defined well enough for compilers to warn if you misapply the label.
It would make sense that (much like inline) the compiler can, in all places, apply rules to determine if, in fact, code could have one of the compile-time keywords applied and, be forced, per language specification, to compile as much as possible as if the compile-time keywords had been applied.
Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied. Compile that function, as if all the functions called had the correct compile-time keywords.
Here is a simple example:
#include <tuple>
consteval std::tuple<int, int> init1(int x, int y)
{
return {x,y};
}
std::tuple<int, int>& foo1()
{
static constinit std::tuple<int, int> x=init1(1,2);
return x;
}
std::tuple<int, int> init2(int x, int y)
{
return {x,y};
}
std::tuple<int, int>& foo2()
{
static std::tuple<int, int> x=init2(1,2);
return x;
}
static std::tuple<int, int> x3=init2(1,2);
std::tuple<int, int>& foo3()
{
return x3;
}
see it here: https://godbolt.org/z/KrzGfnEo7
Note that init1/foo1 are fully specified with compile-time keywords and there is no need for a guard variable, since it is initialized completely (not just 0 filled).
The question is about why the compiler doesn't do the same in the cases of init2/foo2 or x3/foo3? Or more precisely why the compiler is allowed NOT to initialize fully at compile time.
EDIT (as per comment) see (Why do we need to mark functions as constexpr?) constexpr would be required. I am concerned more with cases of consteval and constinit. Could the specification be modified to require trying to resolve code at compile-time and not failing because of the absence of constexpr? This would allow interface contracts to just use constexpr as per the related question.
Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied.
The basis of your question is the assumption that these keywords are just variations on a theme, that a function which could have some of them ought to have a specific one based on the properties within the function alone.
That's not reasonable.
For functions, any function which could be constexpr could also be consteval. The difference is that one can be called during constant expression evaluation, and the other must be called only during constant expression evaluation. This is not an intrinsic property of the function definition; it is about how the function is to be used.
The compiler should not override the decision of a programmer to be able to use a function at runtime just because the function definition just so happens to also be legit for consteval.
It should also be noted that there is the requirements of a function definition for a consteval function are the same as for a constexpr function.
It is also not possible to know if a function definition ought to be constexpr or not. Remember: while a constexpr function explicitly forbids certain constructs from appearing in the definition, it also permits you to have certain constructs in the definition that aren't allowed to be evaluated during constant evaluation... so long as you never actually allow those code paths to be evaluated during constant evaluation. So there are many function definitions that just so happen to be valid for constexpr even if the programmer didn't intend for them to be evaluated at compile-time.
Lambdas get implicit constexpr definitions only because space in a lambda expression is at a premium.
For variables, implicit constexpr makes even less sense. A variable ought to be constexpr if you intend to use it in a constant expression later on. This requires it to have an initializer that is a constant expression. Implicit constexpr is a problem because you might start relying on the implicit constexpr rule, then change the initializer to no longer be a constant expression and get a strange error in the place where you used its constexpr properties.
It's better to make the user be explicit up-front than to misjudge what the user was trying to do and make a user think something is supposed to be valid when it wasn't intended.
And constinit is solely about ensuring that a user never accidentally uses a non-constant expression to initialize the variable. Making it implicit would make absolutely no sense, as changing the expression to no longer be a constant expression wouldn't cause compilation to fail. Compilers are smart enough to know when a variable is initialized with a constant expression and can decide how to handle that on its own.

`if constexpr` vs `if` in light of compiler optimization and code performance

Consider a function template func that is very performance critical. It can be instantiated with T=Type1 or some other type. Part of the function logic depends on T it is instantiated with.
One can either explicitly use a if constexpr (Code B) or use a vanilla if instead (Code A), while compiler probably optimizes the code.
However, I wonder, how the implementation without constexpr (Code A) is any different? Isn't the compiler capable of detecting which branch of if (in Code A) to use at compile time while instantiating? Can it still (for Code A) generate a less efficient code?
Code A. Without if constexpr:
template<class T>
void func(T argument)
{
// some general type-independent logic
if (std::is_same<Type1,T>::value)
{
// do something
}
else
{
// do something else
}
// some general type-independent logic
}
Code B. With if constexpr:
template<class T>
void func(T argument)
{
// some general type-independent logic
if constexpr (std::is_same<Type1,T>::value)
{
// do something
}
else
{
// do something else
}
// some general type-independent logic
}
Both codes A & B compile, as do something and do something else are well-formed for any T.
There are some similar-sounding questions:
Why is constexpr if needed? – this one answers when constexpr is required.
Difference between if and constexpr if – just lists the differences
The aforementioned questions do not answer if Code B is preferable to Code A for some reason (when both branches are well-formed anyway).
The only advantage I see would be to tell the programmer explicitly that this if is compile-time; however, I would say the conditional expression is self-explanatory.
if constexpr is not intended about optimization. Compilers are very good at optimizing away a branch that is if (true) or if (false) (since we're talking about constant expressions, that is what it boils down to). Here is a godbolt demo of the example in OP - you'll note that both gcc and clang, even on -O0, do not emit a branch for a simple if.
if constexpr is all about ensuring that only one branch of the if is instantiated. This is hugely important and valuable for writing templates - because now we can actually write conditionally compiling code within the body of the same function instead of writing multiple artificial functions just to avoid instantiation.
That said, if you have a condition that is a known constant expression - just always use if constexpr, whether or not you need the instantiation benefit. There is no downside to such a decision. It makes it clearer to readers that indeed this condition is constant (since otherwise it wouldn't even compile). It will also force the evaluation of the expression as a constant (a slight variant leads gcc to emit a branch at -O0, thought not at -O1), which with the coming addition of is_constant_evaluated() may become more important in the long run (possibly even negating my opening paragraph).
The only advantage I see would be to tell the programmer explicitly that this if is compile-time; however, I would say the conditional expression is self-explanatory.
To address this specifically, yes, std::is_same<X, Y>::value is "self-explanatory" that it is a constant expression... because we happen to be familiar with std::is_same. But it's less obvious whether foo<X>::value is a constant expression or whether foo<X>() + bar<Y>() is a constant expression or anything more arbitrarily complicated than that.
It's seeing if constexpr that makes the fact that it's compile-time self-explanatory, not the content of the condition itself.
Adding an example to #Barry 's explanation: The use is primarily for writing templates. Consider the following:
template <class T>
auto get_value()
{
if constexpr (std::is_same_v<T, int>) {
return 1
} else {
return 2.0;
}
}
You can note that, if the template parameter is int, the return value is determined to be int, while it is float when the template parameter is not int. You will see that this does not work with non-constexpr if statements, because at instantiation, all returns of a function must have a common type, which the former does not have. The only other way of achieving this is to use c++20 contraints, or std::enable_if to overload the function based on the template parameter.

template arguments inside a compile time unrolled for loop?

wikipedia (here) gives a compile time unrolling of for loop.......
i was wondering can we use a similar for loop with template statements inside...
for example...
is the following loop valid
template<int max_subdomain>
void Device<max_sudomain>::createSubDomains()
{
for(int i=0; i< max_subdomain; ++i)
{
SubDomain<i> tmp(member);
...
// some operations on tmp
...
}
}
SubDomain is a class which takes in the a template parameter int and here has been constructed with an argument that is a member of the Device class.
Thanks for the answer guys...
now that you know what i want...
is there anyway i achieve what i want to??
i finally got what i wanted..............
instead of using the for loop directly...
one can instead use the Boost::MPL for_each construct. I haven't yet implemented it but I am guessing that this provides a way to do what i wanted.....
I took the answer from another stack overflow question here... However the comment to the same question decries its use because it would be very slow (for large for loops of course)... however.. for loops which are not large i don't think there should be any bloating... i'll try the code and let you know the results....
the use is illustrated well in the example
There's a stadard solution for this. Convert iteration into recursion.
template<int i>
void Device::createSubDomains()
{
SubDomain<i> tmp(member);
// some operations on tmp
createSubDomains<i-1>();
}
template<>
void Device<-1>::createSubDomains()
{
// End of recursion.
}
Note: you can't use a runtime if(i!=0) createSubDomains<i-1>();.
2017 Note: you can now use a compile-time if constexpr(i!=0) createSubDomains<i-1>();
Even though you already accepted #iammilind 's answer, let me propose another one, because his reasoning for why i is not compile-time-constant is was not correct.
Suppose you have
template<unsigned int MAX> struct SubDomain {...};
...
and you want to declare an instance of it ...
SubDomain<i> tmp(member);
then i must be a commonly so-called compile-time-constant. What is that?
Pedantry
The standard assigns the term nontype template argument to template arguments that are not types (D'Oh).
14.3.2 Template non-type arguments [temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— ... [more follow, but are not relevant]
Right the first point contains a reference for further research: an integral constant-expression. This leads us to
5.19 Constant expressions [expr.const]
In several places, C + + requires expressions that evaluate to an integral or enumeration constant: as array
bounds (8.3.4, 5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2),
as static member initializers (9.4.2), and as integral or enumeration non-type template arguments (14.3).
Then, the key is:
An integral constant-expression can involve only literals (2.13), enumerators, const variables or static
data members of integral or enumeration types initialized with constant expressions (8.5), non-type template
parameters of integral or enumeration types, and sizeof expressions.
Pedantry application
If we look back at your loop:
for (int i=...
...
SubDomain<i>
then we can now observe that i is not allowed there. Why? Because i is NOT a const variable.
The observing reader might now think you can circumvent this:
for (int i=...
...
const int I = i;
SubDomain<I>
But is this really allowed? Negative, I = i is not an integral constant-expression, because i is not. It helps to realise that the rule for integral constant-expressions is applied recursively.
E.g., the following is valid code:
template <int> void foo() {}
int main () {
const int ccI = 0;
const int ccJ = ccI*2;
const int ccK = ccJ/4;
foo<ccK>();
}
But if make just one part of the chain non-const, then ccK is not considered integral constant anymore:
template <int> void foo() {}
int main () {
int ccI = 0;
const int ccJ = ccI*2; // not compile time constant
const int ccK = ccJ/4; // same
foo<ccK>(); // error
}
Summary
So, in human readable form, template arguments that are not types, but (integer) values, must be compile-time-constant:
the initializer of a compile-time-constant must only involve other compile-time-constants
a literal value is a compile-time-constant
an enum value is a compile-time-constant
function-calls don't give compile-time-constants (for some advanced reasons)
Re-Edit:
My previous answer was correct. I have tried your code, it's giving compiler error. You cannot declare objects like that, as i cannot remain a compile time constant (as you are intending to do i++). template parameter must always be compile time constants. Here is the demo.
Also note that, loop unrolling is done for normal loops also, as part of optimization by compilers. It's not limited to templates.