3
votes

There are similar questions about, but none I've found that directly answer this question.

I want to implement a constexpr function like this:

constexpr int Foo(int x) {
  static const int table[128] = { 3, 1, 4, 1, 5, ..., 99 };
  return (0 <= x && x < 128) ? table[x] : 42;
}

I'm wary of making the table function-static as shown because the compiler might add expensive checks to make initialization of the table thread safe (slowing each call), and those checks might make the optimizer less likely to inline this otherwise trivial function.

So thought I'd move the table to namespace static, defining it in one .cpp file, while the function itself remains defined in a header so that it can be inlined.

constexpr int Foo(int x) {
  extern constexpr int table[];
  return (0 <= x && x < 128) ? table[x] : 42;
}

The compiler complains that I can't declare table inside a constexpr function. So then I tried:

extern constexpr int table[];
constexpr int Foo(int x) {
  return (0 <= x && x < 128) ? table[x] : 42;
}

That's not allowed because you can't just declare something constexpr, you have to define it. But if I define the table in a header file, I'll violate the one-definition rule, right?

constexpr int table[128] = { 3, 1, 4, 1, 5, ..., 99 };
constexpr int Foo(int x) {
  return (0 <= x && x < 128) ? table[x] : 42;
}

I know Foo doesn't violate ODR, because constexpr implies inline for function definitions. The compiler accepts this and seems to do the right thing, but I know compilers aren't required to issue a diagnostic for ODR violations.

Q1: In this last iteration, is table an ODR violation?

Q2: If not, is there a way to prevent table from being visible to every translation unit that includes this header?

1
"I'm wary of making the table function-static as shown because ..." This is simply not a realistic concern with ints due to constant initialization. - ildjarn
@ildjarn: Fair enough, but there are additional motivations for the question. My compiler (VC++2015) won't let me declare the table as function static inside a constexpr function, which I think requires C++14 compliance. - Adrian McCarthy
Fortunately, VC++2017 will at least. :-] - ildjarn
Regarding Q1, constexpr implies const implies static, so yes, it's an ODR violation. In C++17 it can be marked inline to resolve that. - ildjarn

1 Answers

0
votes

Not sure about Q1 (if you define table static?) but, for Q2, I propose Foo() as a friend function for a class where table is a private static constexpr member.

By example

#include <iostream>

class wrapTable
 {
   private:
      static constexpr int table[] { 2, 3, 5, 7, 11, 13, 17, 19 };
      static constexpr int size { sizeof(table)/sizeof(table[0]) };

      friend constexpr int foo (int);
 };

constexpr int wrapTable::table[];

constexpr int foo (int x)
 { return (0 <= x && x < wrapTable::size) ? wrapTable::table[x] : 42; }

int main()
 {
   std::cout << "foo(3): " << foo(3) << std::endl; // print 7
   std::cout << "foo(9): " << foo(9) << std::endl; // print 42

   // compilation error: 'table' is a private member of 'wrapTable'
   // std::cout << "table(3): " << wrapTable::table[3] << std::endl;
 }