The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.
Template argument deduction is used when selecting user-defined conversion function template arguments. A is the type that is required as the result of the conversion. P is the return type of the conversion function template.
We allow annotating a function parameter with constexpr with the same meaning as a variable declaration: must be initialized with a constant expression.
template<int F(), int N = F()> void func ();
In this answer we will go through the relevant sections of the International Standard, step by step, to prove that the above snippet is well-formed.
The Standardese
14.1p9 Template parameters
[temp.param]
A default template-argument is a template-argument (14.3) specified after
=
in a template-parameter. [...]
14.3p6 Template arguments
[temp.arg]
If the use of a template-argument gives rise to an ill-formed construct in the instantiation of a template specialization, the program is ill-formed.
14.3.2p1 Template non-type arguments
[temp.arg.nontype]
A template-argument for a non-type, non-template template-parameter shall be one of:
- for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
- the name of a non-type template-parameter; or
- a constant expression (5.19) that designates the address of an object [...]; or
- a constant expression that evaluates to a null pointer value (4.10); or
- a constant expression that evaluates to a null member pointer value (4.11); or
- a pointer to member expressed as described in 5.3.1
5.19p3 Constant expressions
[expr.const]
A literal constant expression is a prvalue core constant expression of literal type, but not pointer type. An integral constant expression is a literal constant expression of integral or unscoped enumeration type. A converted constant expression of type
T
is a literal constant expression, implicitly converted to the typeT
, [...]
8.3.6p3 Default arguments
[dcl.fct.default]
A default argument shall be specified only in the parameter-declaration-clause of a function declaration or in a template-parameter (14.1); in the latter case, the initializer-clause shall be an assignment-expression.
The Verdict
The above sections makes us come to the following conclusions:
template<int F(), int N = F()>
void func ();
constexpr int (*F)() = <some_initializer>; // (A)
constexpr int N = <explicit_template_argument> OR <F()> // (B)
The snippet above can be used as a mental helper to ease reasoning about what the template-parameters will be equivalent to, given a set of template-arguments.
To see whether (B) is valid or not, where an explicit template-argument is not given for N, we must evaluate (A) - and the evaluation of (A) might yield a value for F that is usable in the constant-expression required by (B).
With that said; Yes, the template is legal C++11.
Legal
constexpr int g () { ... }
// func<&g>
constexpr int (*F)() = &g; // ok
constexpr int N = F(); // ok
Ill-formed
int f () { ... }
// func<&f>
constexpr int (*F)() = &f; // ok
constexpr int N = F(); // ill-formed, not a constant-expression
The same set of rules apply to the following template;
template<int X, int N = 100/X>
void gunc ();
gunc<0> (); // ill-formed, `100/0` is not mathematically defined,
// and is therefore not a constant-expression
For the language-lawyer
And this, pointless use of a default template-argument, is actually legal since F()
might be a constant-expression.
F()
can however not be a converted constant-expression to give N a value, but this doesn't happen until (if ever) the default argument is actually used.
template<void F(), int N = F()>
void hunc ();
void f ();
hunc<&f, 10> (); // legal
hunc<&f > (); // ill-formed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With