Consider the following simplified template meta-programming code that implements an Angle class that is internally storing the modulo 360 degrees reduced value.
#include <iostream>
#include <typeinfo>
template<int N, int D>
struct Modulus
{
static auto const value = N % D;
};
template<int N>
struct Angle
{
static auto const value = Modulus<N, 360>::value; // ERROR
//static int const value = Modulus<N, 360>::value; // OK
//static auto const value = N % 360; // OK
typedef Angle<value> type;
};
int main()
{
std::cout << typeid(Angle<30>::type).name() << "\n";
std::cout << typeid(Angle<390>::type).name() << "\n";
return 0;
}
Output on Ideone
With Visual C++ 2010 Express, I can do static auto const = Modulus<N, 360>::value, but with MinGW gcc 4.7.2 (Nuwen distro) or Ideone (gcc 4.5.1) I have to either explicitly denote the type as static int const value = Modulus<N, 360>::value or I have to use auto with the full modular expression as static auto const value = N % 360;.
Question: Which compiler is correct acccording to the new C++11 Standard?
The code is valid. Visual C++ is right to accept it and gcc is wrong to reject it (for completeness, Clang 3.1 also accepts the code). The specification states that (C++11 7.1.6.4[dcl.spec.auto]/4):
The auto type-specifier can also be used...in declaring a static data member with a brace-or-equal-initializer that appears within the member-specification of a class definition.
Your value is a static data member. It has a brace-or-equal-initializer (that is the = Modulus<N, 360>::value part of the declaration), and the initializer appears within the member-specification of the class definition (i.e., it's what mortals might call an "inline initializer").
Related
I have a following struct:
#include <iostream>
struct Foo
{
static int a; // declaration
inline static int b = 10; // declaration and definition
};
int Foo::a = 10; // definition
int main()
{
std::cout << Foo::a << "\n"; // --> 10
std::cout << Foo::b << "\n"; // --> 10 (same result as Foo::b)
return 0;
}
It is "easier" to use inline static to achieve same goal as when static is used.
Is there any case when using static members for class instead of inline static members is more preferable/required?
Note : there is an obvious case when one uses C++14 or lower standard.
You'd see it if you ever try to create "stateful enums". Basically, classes that capture a little data and there is a handful of "named constants" of that type that you'd want to codify as static data members. To illustrate:
struct RGB {
inline static RGB c1{1};
inline static RGB c2{2};
RGB(int, int = 0, int = 0);
};
This will be ill-formed, whereas this will not:
struct RGB {
static RGB c1;
static RGB c2;
RGB(int, int = 0, int = 0);
};
RGB RGB::c1{1};
RGB RGB::c2{2};
Live example
The reason is that a static data member may have an incomplete type, but it must be complete when initialised.
It will naturally pop up when playing with constexpr static members. Come C++17, they are implicitly inline, so you'd have to break the declaration from definition if you want to have them:
struct RGB {
static const RGB c1;
static const RGB c2;
constexpr RGB(int, int = 0, int = 0){}
};
constexpr RGB RGB::c1{1};
constexpr RGB RGB::c2{2};
Live example
Those are valid constant, and are usable in constant expressions. But they cannot be defined inline as static data members. They are still technically inline data members on account of being constexpr, but are not defined inline (mind though that it's true prior to C++17 too, but the constant cannot be defined in a header for redefinition errors, so they are essentially unusable).
Actual code is more complex but I was able to reduce it to this example.
Everything works fine until I try to take a pointer to MyPackets_t::types (uncomment call to foo() in main() )
At this point in order for the application to link, types requires a definition.
I'm struggling with correct syntax for the definition. Commented out template... should do the trick. However it generetes an error " template arguments to 'PacketCollection::types' do not match original template".
Trying something like this - template<> constexpr int PacketCollection::types[]; - causes another linker error as though variable is being used in that line rather than declared.
I've tried using MyPackets_t instead of PacketCollection - same results.
If I make packet collection non-templated then everything compiles and runs as expected.
I feel that I'm either missing something extremely basic here or there is a bug in the compiler. I get this behavior with gcc 4.8 and 4.9.
Clang 3.5 has a sligtly different take on the situation: declaration of constexpr static data member 'types' requires an initializer.
The only workaround that I've found so far was to use
static constexpr std::array<int, 2> types() { return {1,2}; }
instead, but I don't like this solution. If variable workes in non-templated version (using the initializer from header) it should also work for templated one.
#include <iostream>
using namespace std;
class Packet_A
{
public:
static constexpr int TYPE = 1;
};
class Packet_B
{
public:
static constexpr int TYPE = 2;
};
template <typename T> class PacketCollection
{
public:
static constexpr size_t size = 2;
static constexpr int types[size] { 1, 2 };
};
typedef PacketCollection<Packet_A> MyPackets_t;
//template<typename T> constexpr int PacketCollection<Packet_A>::types[PacketCollection<Packet_A>::size];
void foo(const int* bar, size_t size)
{
if (size >= 2)
{
cout << bar[0] << bar[1];
}
}
int main(int argc, char* argv[])
{
cout << Packet_A::TYPE;
cout << MyPackets_t::types[0];
//foo(MyPackets_t::types, MyPackets_t::size);
return 0;
}
You should use T instead of Packet_A
template<typename T> constexpr int PacketCollection<T>::types[PacketCollection<T>::size];
^ ^
See live example.
Following up with this question Having a constexpr static string gives a linker error
In the question, this code wasn't able to compile:
#include <iostream>
struct Test { static constexpr char text[] = "Text"; };
int main()
{
std::cout << Test::text << std::endl; // error: undefined reference to `Test::text'
}
From the comment, this code is able to compile:
#include <iostream>
struct Test { static constexpr auto text = "Text"; };
int main()
{
std::cout << Test::text << std::endl;
}
My question is why the auto version works but the array of char version doesn't?
Could you please point out the statement in the standard allowing the second version and disallowing the first?
I took a look at Strange behavior with constexpr static member variable but it seems to be another question.
A declaration of a static data member in class is never a definition.
The difference between your examples is that only one requires a definition of text.
The auto version deduces char const*, hence text is only subject to an lvalue-to-rvalue conversion and not odr-used. By contrast, the first code effectively passes text's address, odr-use-ing it - i.e. necessitating a definition.
struct Test { static constexpr auto text = "Text"; };
resolves to
struct Test { static constexpr const char * text = "Text"; };
So the second expression is a constexpr value of a pointer not an array.
Background
What I'm trying to achieve: I'm trying to implement something like a Java enum (ie. enum that has some additional functionality). I came up with a solution using two classes, where one class represents a value and the other acts as an enumeration of possible values using static variables to represent each value. I want it to be a real replacement of enum, including the possibility to use the enum values in template instantiation. For this to work, an enum value needs to be a constant expression (constexpr). However, I'm not sure if I use the constexpr correctly.
The Code
Here is the code that I came up with:
class EnumType {
public:
enum Enum {val_A, val_B, val_C};
friend class EnumTypeList;
EnumType()
: m_ID(val_A), m_foo(0) {}
constexpr operator Enum() const {return m_ID;};
constexpr unsigned int getFoo() const {return m_foo;};
protected:
constexpr EnumType(const Enum v, const int foo)
: m_ID(v), m_foo(foo) {}
private:
Enum m_ID;
int m_foo;
};
class EnumTypeList {
public:
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
static constexpr EnumType B = EnumType(EnumType::val_B, 4);
static constexpr EnumType C = EnumType(EnumType::val_C, 8);
};
The class EnumType holds the information of each value and provides some additional functions (here it is storing the additional value m_foo that can be accessed using getFoo() function). The enumeration itself is represented by the EnumTypeList that contains static constexpr variables, where each variable represents a possible value of an enum. This code allows me to use the variables from EnumTypeList in place of EnumType::Enum. Even in templates like in the following code:
template <EnumType::Enum T>
class Test {
public:
void test() {
std::cout << "Enum is not A" << std::endl;
}
};
template <>
class Test<EnumType::val_A> {
public:
void test() {
std::cout << "Enum is A" << std::endl;
}
};
int main() {
Test<EnumTypeList::A> a;
a.test();
Test<EnumTypeList::B> b;
b.test();
// this shouldn't compile
/*EnumType x = EnumTypeList::C;
Test<x> c;
c.test();*/
}
This works as I would expect – I can use the values from EnumTypeList in place of EnumType::Enum in template instantiation as demonstrated above, but I can't do it with EnumType x = EnumTypeList::C;
The Problem
While the code compiles correctly without any warning under gcc and clang, I'm not sure if I use the constexpr correctly. The thing is that while the EnumType constructor and the conversion operator operator Enum() are constexpr, they both access variables m_ID and m_foo that are not constant (because I need assignment operator).
This is fine, members of literal types are allowed to be modifiable.
In order to use the type in a constant expression you must construct it with constant arguments, but that's OK because you do that;
static constexpr EnumType A = EnumType(EnumType::val_A, 5);
The object constructed is a valid constant expression so can be used to initialize the constexpr variable A. You don't modify the members of the object, so it doesn't matter that they are modifiable.
Clang in particular is very strict about constant expressions, if you were doing something wrong it would give an error.
This object can be used where a constant expression is needed:
constexpr EnumType A5(EnumType::val_A, 5);
e.g.
constexpr int i = A5.getFoo();
This object cannot:
EnumType A6(EnumType::val_A, 6);
constexpr int i = A6.getFoo(); // error
I'm having trouble with the following code:
template<typename T>
constexpr int get(T vec) {
return vec.get();
}
struct coord {
constexpr int get() const { return x; }
int x;
};
struct foo {
struct coord2 {
constexpr int get() const { return x; }
int x;
};
constexpr static coord f = { 5 };
constexpr static int g = get(f); // works
constexpr static coord2 h = { 5 };
constexpr static int i = get(h); // doesn't work
};
constexpr coord foo::f;
constexpr foo::coord2 foo::h;
int main(){}
Essentially, get(f) is considered a constant expression, but get(h) is not. The only thing changed is that one uses a global struct coord, while the other uses a nested struct coord2. The structs'
bodies are identical.
Why is this?
GCC error:
test.cpp:20:35: error: field initializer is not constant
Clang error:
test.cpp:20:26: error: constexpr variable 'i' must be initialized by a constant expression
constexpr static int i = get(h); // doesn't work
^ ~~~~~~
test.cpp:8:10: note: undefined function 'get' cannot be used in a constant expression
return vec.get();
^
test.cpp:20:30: note: in call to 'get({5})'
constexpr static int i = get(h); // doesn't work
^
test.cpp:13:21: note: declared here
constexpr int get() const { return x; }
It is a constant expression.... eventually, as this shows you can see by moving i into main():
http://ideone.com/lucfUi
The error messages are pretty clear what's going on, which is that foo::coord2::get() isn't defined yet, because member function definitions are delayed until the end of the enclosing class so that they can use members declared later.
It's a little surprising that the definition is delayed until the end of the outermost enclosing class, but you'd be even more surprised if foo::coord2::get() couldn't access foo::g.
The Standard agrees with the compiler, btw. Part of section 9.2p2 says
Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and brace-or-equal-initializers for non-static data members (including such things in nested classes).
Unfortunately, it's only inferred that the closing brace of the class declaration becomes the point-of-definition for these deferred regions. I believe it's a defect in the Standard that it doesn't say this explicitly.
See also:
https://stackoverflow.com/a/11523155/103167