4

I am looking for a portable one line replacement for the #define in the following code. The replacement should hide the word APPLE in the namespace of the Foo object.

class Foo { public: #define APPLE 123 Foo(int a) : a_(a) { } }; // elsewhere in another file Foo f(APPLE); 

I tried to make this more C++ friendly this, and it worked using the Intel 2017 compiler:

class Foo { public: static constexpr int APPLE = 123; Foo(int a) : a_(a) { } }; // elsewhere Foo a(Foo::APPLE); 

but it does not work with g++ ((GCC) 6.3.1 20170216), because it gives the error

undefined reference to Foo::APPLE 

because it is probably trying to take a reference to APPLE.

I know I can "fix" the problem by creating definition in a *.cpp file of

constexpr int Foo::APPLE; 

but that violates my ideal of having the #define be replaced by 1 line. My Foo class is header-file only, and now I would need a cpp file just for the definition of Foo::APPLE. I know I could also declare APPLE as a function (static constexpr int APPLE() {return 123;}) but that is a whole lot more typing in the declaration and at every point of use I need to call the function with ().

It seems easier just to use the #define and be done with it. Non-static const int works fine, but APPLE is not usable as an argument to the constructor. Maybe there is a good reason why this is impossible in C++.

Edit: This is not a duplicate of Undefined reference to static constexpr char[]. That question is related to a string and why the particular error message comes up. I am trying to avoid using static linkage all together (I acknowledge in my question that I am aware of how to do static linkage) and I want to do it a "better/cleaner" way, and I see from the Answers that the way that passes my criteria is to use enum.

8
  • 1
    Can you use C++17? C++17 has inline variables for this reason. Commented Oct 18, 2018 at 23:35
  • 2
    C style? enum { APPLE = 123 }; Commented Oct 18, 2018 at 23:35
  • 1
    @MarkLakata Then the enum is likely your best option unfortunately. Not ideal, but it works. Commented Oct 18, 2018 at 23:49
  • 1
    Possible duplicate of Undefined reference to static constexpr char[] Commented Oct 18, 2018 at 23:50
  • 1
    static constexpr int APPLE = 123; requires a storage allocation and takes up space. You can take the address of APPLE. Use an enum instead. It is effectively a symbolic constant (but you can't take the address of it). Commented Oct 19, 2018 at 3:41

2 Answers 2

6

You've already listed most alternatives in your question. You'll need consider which approach you want to take:

  • Use inline variable which requires C++17 (your first attempt works implicitly in this standard)
  • Define the static member in a source file, which you don't want to do
  • Use an inline static member function instead, which you also don't want
  • Use a namespace scoped constexpr variable instead of a member
  • Use a member enum: enum : int { APPLE = 123 };
  • Use the macro (don't pick this)
Sign up to request clarification or add additional context in comments.

1 Comment

The enum method is the only one that works for me. Thanks
2

In addition to the things mentioned already, there's also "poor man's inline variables":

static constexpr const int& APPLE = std::integral_constant<int, 123>::value; 

You define a class template with a constant static data member whose value is what you want. You define that static data member out-of-line - but in the header, since it's a static data member of a class template. In this case, std::integral_constant does all that already, so you don't have to write your own.

Then, you define your actual static data member constant as a constexpr reference to that class template static data member; no out-of-line definition is needed because it is not possible to odr-use a reference initialized by a constant expression.

2 Comments

Thanks for the answer. I was not aware of the existence of integral_constant and the fact that template exists at all doesn't help me like C++ any more. What problem was that intending to solve? (I understand it sort of solves this problem, but doesn't this put a huge burden on the optimizer to figure out that APPLE can be treated as a literal?
integral_constant is primarily used in template metaprogramming to encode a compile-time constant into a type. Using it here is convenient, but it's hardly its intended use. This is trivial constant propagation that the compiler should have little problems with.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.