Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++14: Initializing constexpr variables from parameter values

Say I have a class that that can return a constant expression through a constexpr function:

template<int N>
struct Foo {
  constexpr int Bar() const { return N; }
};

If I wanted to initialize constexpr values from Foo::Bar(), how should I pass a parameter of type Foo? I've tried these two, with an example constexpr variable inside of each to test that it can be initialized:

template<int N>
constexpr int ByValue(Foo<N> f) {
  constexpr int i = f.Bar();
  return f.Bar();
}

template<int N>
constexpr int ByReference(const Foo<N> &f) {
  constexpr int i = f.Bar();
  return f.Bar();
}

constexpr int a = ByValue(Foo<1>{});
constexpr int b = ByReference(Foo<1>{});

But clang 3.7 raises an error on ByReference while gcc >=5.1 does not: Live demo

main.cpp:15:25: error: constexpr variable 'i' must be initialized by a constant expression
      constexpr int i = f.Bar();
                        ^~~~~~~
main.cpp:22:25: note: in instantiation of function template specialization 'ByReference<1>' requested here
      constexpr int b = ByReference(Foo<1>{});

What's the difference between taking a const Foo & or a plain Foo, when Bar is constexpr either way and returns a valid constant expression?

Which is right and why, GCC or Clang? If available, references to the standard would be appreciated.

like image 281
Xo Wang Avatar asked Jul 30 '15 03:07

Xo Wang


People also ask

When to use #define vs constexpr?

#define (also called a 'macro') is simply a text substitution that happens during preprocessor phase, before the actual compiler. And it is obviously not typed. constexpr on the other hand, happens during actual parsing. And it is indeed typed.

Does constexpr need to be static?

A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later. So, what does constexpr mean?

How do I declare constexpr?

To be constexpr, a function must be rather simple: just a return-statement computing a value. A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression.

Is constexpr can be used with #define macros?

No. Absolutely not. Not even close. Apart from the fact your macro is an int and your constexpr unsigned is an unsigned , there are important differences and macros only have one advantage.


1 Answers

§5.20:

enter image description here

The reference does not have a preceding initialization from the point of view of i, though: It's a parameter. It's initialized once ByReference is called.

Let's remove the constexpr from i's declaration and consider an invocation of ByReference in its entirety:

template<int N>
constexpr int ByReference(const Foo<N> &f) {
    int i = f.Bar();
    return i;
}

constexpr int j = ByReference(Foo<0>());

This is fine, since f does have preceding initialization. The initializer of f is a constant expression as well, since the implicitly declared default constructor is constexpr in this case (§12.1/5).
Hence i is initialized by a constant expression and the invocation is a constant expression itself.

like image 193
Columbo Avatar answered Sep 24 '22 18:09

Columbo