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.
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)
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.
Related
I have a situation where I need to store a compile time constant in a header, and I do it in the class I'm using it in as I don't want to expose the constant to other files that include this header. (as well as the fact that it depends on another hidden struct)
Like this:
namespace ns {
class bla {
private:
struct internalStruct {
// ...
};
// I put it in the class as I don't want other files to be able to see this
constexpr const size_t compileConstant = sizeof(internalStruct) * 8;
};
}
The problem is I get a
Constexpr is not valid here
error. The solution is to add static, however I read that constexpr integral members should be inline.
What should I do?
Adding static should be perfectly fine for your use case. That's what the reference states too:
A constexpr specifier used in a function or static data member (since
C++17) declaration implies inline.
And since this is a compile-time constant, you might as well have it shared across all class instances rather than on a per-instance basis (which is usually how even const variables are used).
The following code produces an undefined reference to 'Test::color'.
#include <iostream>
struct Color{
int r,g,b;
};
void printColor(Color color) {
//printing color
}
class Test {
static constexpr Color color = {242,34,4};
public:
void print(){
printColor(color);
}
};
int main() {
Test test;
test.print();
return 0;
}
Why does this code produce the above error and what is the best way to avoid it, considering I want to use the latest version of the standard, C++17?
Should I define the static member variable, just like it was needed in earlier revisions of the standard (see the first answer here: Undefined reference to static constexpr char[]) or should I just create a new Color structure as can be seen below?
printColor(Color{color.r, color.g, color.b});
Edit:
I'm using CLion on Ubuntu 16.04, which as far as I could find out, uses g++ 5.4 for compiling. I have set it to use C++17 and still get the same error. The error is only present when color is passed to a function.
This is due to the fact that, before C++17, you had to specifically define the static variable outside the class:
class Test {
/* ... etc etc ... */
}
const constexpr Color Test::color;
The constexpr-ness of the static member does not let you 'waive' this explicit definition requirement.
With C++17, you no longer need to define static members explicitly. They are implicitly "inline" variables, which get auto-defined at some point, and just once per binary, without you having to take care of it. See here for the long proposal of this feature.
Note that the definition must only appear in a single translation unit (so probably not in a header with class Test that gets included a lot).
The problem was neither with the code itself, nor with the standard being used. CLion's default compiler does not fully support C++17, so that's why it showed a strange behavior that it could compile static constexpr member variables, but only as long as they were not passed to functions.
After updating to the most recent compiler version, I was able to run the code successfully without any changes.
Thank you for all your contribution.
I have a big project and work on refactoring it. Major task is rewrite of logger. New logger is (as far as I can tell) API-compatible with old one, so I believed that after changing header include directory, recompile and relink everything should work. But no. I get multiple errors of the kind undefined reference to <static_data_member>. I can't paste actual code, but it looks for example like this:
// Foo.h
class Foo {
static const int bar = 0;
int baz; // assigned in c-tor
void updateBaz() { baz = bar; }
// ....
}
static const int bar is NOT defined in Foo.cpp. It is sometimes printed by log macros. And it used to work (with old logger), now I have to define it. What change could have caused it?
Another example that that occurs with variables declared by boost:
(...)/blog_adaptor.h:50: error: undefined reference to bbost::serialization::version<CA::CReyzinSignature>::value'
So: when are definitions to static members required and when can they be omitted?
Unless the variables are declared inline (a C++17 feature), definitions of static member variables are not optional, as far as the C++ standard is concerned. Failure to provide a definition is undefined behavior.
Compilers and linkers may vary on exactly what will make them check to see if definitions exist, but that is the nature of undefined behavior.
As Nicol Bolas answered, the code in my project had undefined behavior because static data members were initialized but nowhere defined. To summarize and extend:
Static data member doesn't need to be defined when:
It is not used or is used only in discarded branches (non-instantiated templates and discarded branches of constexpr-if)
in C++17 if member is inline
also clang-tidy says that "out-of-line definition of constexpr static data member is redundant in C++17 and is deprecated", so probably static constexpr also doesn't need it
Further, following code shows why my bad project was not triggering linker error before. I don't know whether it is "not odr-use" or "Undefined Behavior that doesn't hurt you yet":
#include <boost/serialization/version.hpp>
class Klass {};
//BOOST_CLASS_VERSION(Klass, 3);
// would be expanded to:
namespace boost { namespace serialization {
template<>
struct version<Klass> {
static const int value = 3; // not defined anywhere
};
} }
int foo (int val) { // was used by old logger
return val;
}
int bar (const int &val) { // is used by new logger
return val;
}
int main () {
// return bar(boost::serialization::version<Klass>::value); // link error
return foo(boost::serialization::version<Klass>::value); // works fine
}
So there is no link error if member is used but not it's address is not queried. Passing value by reference qualifies as querying the address.
I got 17 integer constants that I'd like to have as private in my class. Is it really necessary to use initialization list?
I read somewhere that I can assign values to constants in the header file, but it doesn't seem to work. I get this error message:
sorry, unimplemented: non-static data member initializers
Is it really necessary to use initialization list?
In modern (2011-era) C++, no. In older versions of the language, yes.
Your error message is apologising that your compiler doesn't support the new initialisation syntax for non-static members yet. Assuming you're using GCC (since I recognise that error from that compiler), then according to this page you'll need to upgrade to at least version 4.7 for that feature.
Alternatively, since they're private and constant, you might consider moving them out of the class into a local namespace in the class's implementation file. Of course, that will only work if you don't need to access them from any inline member functions.
Finally, if they have the same values for all instances of the class (which is likely, since they are const and you're able initialise them independently of the constructor arguments), you could declare them static. Then they can be initialised in their declaration (although older compilers might only allow that if they have an integer type). However, if they are odr-used (roughly speaking, if you need to take a pointer or reference to them), then they will also need to be defined in exactly one source file.
.h:
class MyClass {
public:
MyClass();
~MyClass();
int doSomething();
private:
const int m_newint = 1;
const int m_dosomething = 2;
};
.cc:
MyClass::MyClass() {}
MyClass::~MyClass() {}
int MyClass::doSomething() {
return m_dosomething;
}
Is valid C++11 code, make sure your compiler is set to -std=c++11 to use the
feature.
in the below program i have used static const int init. But it is throwing error
/tmp/ccEkWmkT.o(.text+0x15d): In function check::operation()':
: undefined reference tocheck::init'
This error is coming only when used with vector. Can someone please help? what is the exact behaviour??
#include<vector>
#include<iostream>
using namespace std;
class check{
static const int init=1;
public:
check(){}
void operation();
};
void check::operation(){
vector<int> dummy;
dummy.push_back(init);
}
int main(){
check ck;
ck.operation();
}
"what is the exact behaviour?"
The problem is that push_back takes a reference parameter. You can use the value of a static const int member variable without providing a separate definition of the object, but you can't use a reference to the object itself (since it doesn't exist). The meaning of "using" the member itself is defined in the section of the standard on the One Definition Rule, 3.2/2.
One fix is to provide a definition in exactly one translation unit:
const int check::init;
If you do this, you can also choose to move the = 1 initialization from the declaration (inside the class) to the definition (outside the class).
Another fix is to create a temporary from the member variable (this only uses the value, it doesn't care where the object is located and hence doesn't care whether it exists), then pass a reference to the temporary:
dummy.push_back(int(init));
Of course there's a potential maintenance issue there, that if the types of init and dummy both change to, say, long long[*], and the value changes from 1 to something bigger than INT_MAX, then you're in trouble. For that reason you could use +init, since the unary + operator also creates a temporary for its result. Readers and future maintainers might be a bit puzzled by it, though.
[*] Supposing your implementation has long long.
You've to provide the definition of the static member outside the class (in .cpp file) as:
//check.h (same as before)
class check
{
static const int init=1; //declaration and in-class initialization
public:
check(){}
void operation();
};
Then in check.cpp file, do this:
//check.cpp
#include "check.h"
const int check::init; //definition
If you pass it by reference it is "used" and you may have to define in the .cpp file so that it gets an address.
If you just use the value of the constant, you might get away with not defining it.