Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is address of global variable constexpr?

Consider following

struct dummy{};

dummy d1;
dummy d2;

template<dummy* dum>
void foo()
{
    if (dum == &d1)
        ; // do something
    else if (dum == &d2)
        ; // do something else
}

Now, it is possible to call foo like this

foo<&d1>();
foo<&d2>();

and everything works as expected. But following does not

constexpr dummy* dum_ptr = &d1;
foo<dum_ptr>();

With this error from Visual studio

error C2975: dum_ptr: invalid template argument for foo, expected compile-time constant expression

While this works

constexpr dummy& dum_ref = d1;
foo<&dum_ptr>();

In visual studio, but not in G++, because of

note: template argument deduction/substitution failed:
error: & dum_ref is not a valid template argument for dummy* because it is not the address of a variable

foo<&dum_ref>();

EDIT:
Since C++17, std::addressof is being marked as constexpr, so I would guess it should work.

like image 510
Zereges Avatar asked Dec 11 '16 10:12

Zereges


People also ask

Is constexpr global?

constexpr implies const and const on global/namespace scope implies static (internal linkage), which means that every translation unit including this header gets its own copy of PI.

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 can be constexpr?

constexpr indicates that the value, or return value, is constant and, where possible, is computed at compile time. A constexpr integral value can be used wherever a const integer is required, such as in template arguments and array declarations.

Why constexpr instead of define?

#define directives create macro substitution, while constexpr variables are special type of variables. They literally have nothing in common beside the fact that before constexpr (or even const ) variables were available, macros were sometimes used when currently constexpr variable can be used.


1 Answers

GCC is right on this one.

The expressions are definitely constant-expressions*, since they are assigned to a constexpr variable. However, until c++14, there are additional restrictions on what is allowed for a pointer template argument.

C++14 draft N4140 [temp.arg.nontype]

1 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 a complete object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as &id-expression, where the id-expression is the name of an object or function, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; 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; or a constant expression of type std::nullptr_t.

For foo<dum_ptr>(), dum_ptr isn't expressed as &name, and for foo<&dum_ref>(), dum_ref isn't the name of the object, it's the name of a reference to the object, so both are disallowed as template arguments.

These restrictions are lifted in c++17 to allow any constexpr, so thats why it works there:

C++17 draft N4606 - 14.3.2 Template non-type arguments [temp.arg.nontype]

1 A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • (1.1) a subobject (1.8),
  • (1.2) a temporary object (12.2),
  • (1.3) a string literal (2.13.5),
  • (1.4) the result of a typeid expression (5.2.8), or
  • (1.5) a predefined __func__ variable (8.4.1).

As usual, clang gives the best error messages: https://godbolt.org/g/j0Q2bV


*(see Address constant expression and Reference constant expression)

like image 82
Joseph Ireland Avatar answered Nov 19 '22 09:11

Joseph Ireland