Consider the following code:
struct Foo
{
mutable int m;
template<int Foo::* member>
void change_member() const {
this->*member = 12; // Error: you cannot assign to a variable that is const
}
void g() const {
change_member<&Foo::m>();
}
};
Compiler generates an error message. The thing is that the member m is mutable therefore it is allowed to change m. But the function signature hides mutable declaration.
How to decalre pointer-to-mutable-member to compile this code?
If it is impossible please link to Standard C++.
This code is ill-formed according to C++ Standard 5.5/5:
The restrictions on cv-qualification,
and the manner in which the
cv-qualifiers of the operands are
combined to produce the cv-qualifiers
of the result, are the same as the
rules for E1.E2 given in 5.2.5. [Note:
it is not possible to use a pointer to member that refers to a mutable
member to modify a const class
object.
For example,
struct S {
mutable int i;
};
const S cs;
int S::* pm = &S::i; // pm refers to mutable member S::i
cs.*pm = 88; // ill-formed: cs is a const object
]
You could use wrapper class to workaround this problem as follows:
template<typename T> struct mutable_wrapper { mutable T value; };
struct Foo
{
mutable_wrapper<int> m;
template<mutable_wrapper<int> Foo::* member>
void change_member() const {
(this->*member).value = 12; // no error
}
void g() const {
change_member<&Foo::m>();
}
};
But I think you should consider redesign your code.
Related
I have a class for local use only (i.e., its cope is only the c++ file it is defined in)
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
All the code reside in the same c++ file. When compiling using VS on windows, there is no problem at all.
However, when compiling on Linux I get the undefined reference error only for the second statement.
Any suggestions?
std::min<int>'s arguments are both const int&(not just int), i.e. references to int. And you can't pass a reference to A::MY_CONST because it is not defined (only declared).
Provide a definition in the .cpp file, outside the class:
class A {
public:
static const int MY_CONST = 5; // declaration
};
const int A::MY_CONST; // definition (no value needed)
// initialize static constants outside the class
class A {
public:
static const int MY_CONST;
};
const int A::MY_CONST = 5;
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
To explain what's happening here:
You declared static const integer inside class, this "feature" is here to be able to use it as constant expression,i.e. for local array size, template non-type parameters, etc.. If compiler wants to use this constant expression it must be able to see it's value in that translation unit.
9.5/3
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class
definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression is a constant expression (5.19). A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. — end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
odr-used means to form reference to that variable or take it's address.
std::min takes it's parameters by reference, so they are odr-used.
Solution:
Define it!
class A
{
static const int a = 5;
};
const int A::a; //definition, shall not contain initializer
You can also save the const value to a local variable.
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error: undefined reference to `A::MY_CONST`
int l = std::min<int>( j, b); // works
}
I am having a very strange situation
template<class T> class Strange {
public:
static const char gapchar='-';
};
template<class T> void Strange<T> method1 {
char tmp = gapchar;
}
template<class T> void Strange<T> method2 {
char tmp = gapchar;
}
I include the above class, it has been working for several years.
I added another method, essentially the same signature and just reading the gapchar.
I got undefined error only for the third method, even I am using all three methods.
Then I changed the way I initialize the static variable by
not initializing in the class definition:
static const char gapchar;
template<class T> const char Strange<T>::gapchar='-';
This solved the problem. I could not figure out why the old way of
initializing int or char type (the only two types allowed) inside the class
definition section stop working for only one of the methods but not others.
If you are using some header-only thing and want to avoid having to add a .cpp file, it seems like you can do this:
class A {
public:
static inline const int MY_CONST = 5;
};
(or `static inline constexpr`)
This requires C++17
I am using g++4.8.0, which doesn't contain earlier constexpr bug. Thus below code works fine:
constexpr int size() { return 5; }
int array[size()];
int main () {}
However, if I enclose both the variable inside a class as static, then it gives compiler error:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main () {}
Here is the error:
error: size of array ‘array’ is not an integral constant-expression
Is it forbidden to use constexpr in such a way or yet another g++ bug?
Yes, it is ill-formed. Here's why:
A constexpr function needs to be defined (not just declared) before being used in a constant expression.
So for example:
constexpr int f(); // declare f
constexpr int x = f(); // use f - ILLEGAL, f not defined
constexpr int f() { return 5; } // define f, too late
function definitions inside a class specifier (as well as initializers and default parameters) are essentially parsed in an order like they were defined outside the class.
So this:
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
Is parsed in this order:
struct X {
constexpr inline static int size(); // function body defered
static const int array[size()]; // <--- POINT A
};
constexpr inline int X::size() { return 5; }
That is, parsing of function bodies are defered until after the class specifier.
The purpose of this deferral of function body parsing is so that function bodies can forward reference class members not yet declared at that point, and also so they can use their own class as a complete type:
struct X
{
void f() { T t; /* OK */ }
typedef int T;
};
Compared to at namespace scope:
void f() { T t; /* error, T not declared */ }
typedef int T;
At POINT A, the compiler doesn't have the definition of size() yet, so it can't call it. For compile-time performance constexpr functions need to be defined ahead of their use in the translation unit before being called during compile, otherwise the compiler would have to make a multiple passes just to "link" constant expressions for evaluation.
Apparently it's not even a bug, because its status is RESOLVED INVALID, which means that the people behind GCC and that bugzilla, after reviewing the problem, don't think that this is a GCC bug.
I remind you on that page because there is also an answer for this behaviour in one of the related posts.
I just wanted to add that even though this may not be good practice and will restrict you to defining the class body in the same compilation unit that its declared, it's possible to force the compiler to compile the definition of the function bodies at the same point as its declaration by adding a redundant template parameter:
template <typename = void>
struct X {
constexpr static int size() { return 5; }
static const int array[size()];
};
int main()
{
X<> x{};
...
}
I am trying to understand the trailing return based new function declaration syntax in C++11, using decltype.
In the following code, I am trying to define a member function returning a const & to allow for read-only access to i
#include <iostream>
#include <type_traits>
struct X {
int &i;
X(int &ii) : i(ii) {}
// auto acc() const -> std::add_const<decltype((i))>::type { return i; } // fails the constness test
auto acc() const -> decltype(i) { return i; } // fails the constness test
// const int &acc() const { return i; } // works as expected
};
void modify_const(const X &v) {
v.acc() = 1;
}
int main() {
int i = 0;
X x(i);
modify_const(x);
std::cout << i << std::endl;
return 0;
}
As mentioned in the comments, only the last commented version of acc() works, whereas using the others, the code just compiles and prints value 1.
Question: How do we have to define the acc() function using the new function declaration syntax based on decltype, such that the compilation here fails due to returning a const &int in modify_const, or in other words, such that acc() has a proper const &int return type.
Remark: using int i; instead of int &i; as the member variable in X produces a compile error, as expected.
Edited to better distinguish between constness of v and X::i, respectively. It is the latter I am trying to impose in acc().
The problem is that decltype((i)) return int& and apply const to that type has no effect. You want something like
template <typename T> struct add_ref_const { typedef T const type; };
template <typename T> struct add_ref_const<T&> { typedef T const& type; };
... and then use
auto acc() const -> typename add_ref_const<decltype((i))>::type { return i; }
That is, the const needs to go between the type T and the &. The solution would have been obvious if you had put the const into the correct location: const should go to the right.
There's nothing illegal about a const member function modifying the target of a pointer to non-const, even if that pointer was gotten from a member variable.
From the compiler's perspective, int& IS the correct return type.
Your "modify_const" function is incorrectly named. i is what gets modified, and is not const.
Simply add an & to the left and skip the trailing return type.
struct X {
int &i;
X(int &ii) : i(ii) {}
auto& acc() const { return i; } // Returns const reference
auto& acc() { return i; } // Returns non-const reference
const auto& acc() const { return i; } // Add const to the left to make it even more readable
};
Note that using this syntax you can declare the member variable after you have declared the function.
I have a class for local use only (i.e., its cope is only the c++ file it is defined in)
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
All the code reside in the same c++ file. When compiling using VS on windows, there is no problem at all.
However, when compiling on Linux I get the undefined reference error only for the second statement.
Any suggestions?
std::min<int>'s arguments are both const int&(not just int), i.e. references to int. And you can't pass a reference to A::MY_CONST because it is not defined (only declared).
Provide a definition in the .cpp file, outside the class:
class A {
public:
static const int MY_CONST = 5; // declaration
};
const int A::MY_CONST; // definition (no value needed)
// initialize static constants outside the class
class A {
public:
static const int MY_CONST;
};
const int A::MY_CONST = 5;
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error:
// undefined reference to `A::MY_CONST`
}
To explain what's happening here:
You declared static const integer inside class, this "feature" is here to be able to use it as constant expression,i.e. for local array size, template non-type parameters, etc.. If compiler wants to use this constant expression it must be able to see it's value in that translation unit.
9.5/3
If a non-volatile const static data member is of integral or enumeration type, its declaration in the class
definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression is a constant expression (5.19). A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [ Note: In both
these cases, the member may appear in constant expressions. — end note ] The member shall still be defined
in a namespace scope if it is odr-used (3.2) in the program and the namespace scope definition shall not
contain an initializer.
odr-used means to form reference to that variable or take it's address.
std::min takes it's parameters by reference, so they are odr-used.
Solution:
Define it!
class A
{
static const int a = 5;
};
const int A::a; //definition, shall not contain initializer
You can also save the const value to a local variable.
class A {
public:
static const int MY_CONST = 5;
};
void fun( int b ) {
int j = A::MY_CONST; // no problem
int k = std::min<int>( A::MY_CONST, b ); // link error: undefined reference to `A::MY_CONST`
int l = std::min<int>( j, b); // works
}
I am having a very strange situation
template<class T> class Strange {
public:
static const char gapchar='-';
};
template<class T> void Strange<T> method1 {
char tmp = gapchar;
}
template<class T> void Strange<T> method2 {
char tmp = gapchar;
}
I include the above class, it has been working for several years.
I added another method, essentially the same signature and just reading the gapchar.
I got undefined error only for the third method, even I am using all three methods.
Then I changed the way I initialize the static variable by
not initializing in the class definition:
static const char gapchar;
template<class T> const char Strange<T>::gapchar='-';
This solved the problem. I could not figure out why the old way of
initializing int or char type (the only two types allowed) inside the class
definition section stop working for only one of the methods but not others.
If you are using some header-only thing and want to avoid having to add a .cpp file, it seems like you can do this:
class A {
public:
static inline const int MY_CONST = 5;
};
(or `static inline constexpr`)
This requires C++17
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