Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Must template argument functions be treated as potentially constexpr?

People also ask

How do I know if a function is constexpr?

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.

What is template argument deduction?

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.

Can a function parameter be constexpr?

We allow annotating a function parameter with constexpr with the same meaning as a variable declaration: must be initialized with a constant expression.


Introduction

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.


What does the International Standard (N3337) say?

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 type T, [...]

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:

  • A default template-argument is a template-argument, and;
  • when instantiating a template, all template-arguments must be usable in the context where they appear, and;
  • every template-argument for a non-type, non-template template-parameter that appears in a program must be a literal constant expression, and;
  • the default-argument for a template-parameter shall be an assignment-expression.

The Explanation

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

Bonus

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