I get some errors. I need pass a member to a const member with operator =.
I don't know what is error, my function declaration is:
template<class I> forceinline
void OverweightValues<I>::init(int t,
SharedArray<int>& elements0,
SharedArray<int>& weights0,
I& i) {
{..}
//both are declared as 'const SharedArray<int> elements, weights;'
//but elements0 and weights0 not is const
elements = elements0;
weights = weights0;
When I compile get the error:
In line(16): no operator "=" matches these operands operand types are: const SharedArray<int> = SharedArray<int>
In line(17): no operator "=" matches these operands operand types are: const SharedArray<int> = SharedArray<int>
How can I fix this?
const members can only be set during creation. You'll need to use the constructor initializer list rather than two-phase construction. (Even the constructor body cannot assign them)
As #Ben Voigt mentioned, you can only initialize a const in the constructor initialization list. This is because, by the time the control reaches the constructor body all the class members are already created by the compiler using the default constructor.
Hence, member initialization within the constructor body will actually invoke the assignment constructor which cannot be done for constants, references or objects for which default constructor is not available.
Related
As self-exercise, I have written this simple code:
#include <iostream>
int gIndex = 3;
template <class T> class Array
{
public:
explicit Array(int size);
T& operator[](int i) { return m_data[i]; }
T operator[](int i) const { return m_data[i]; }
T getAnchorPoint() const { return m_data[m_anchor]; }
private:
T* m_data;
int m_size;
int& m_anchor;
};
template <class T> Array<T>::Array(int size) : m_size(size), m_data(new T[size])
{
memset(m_data, 0, size*sizeof(T));
m_anchor = gIndex;
}
int main()
{
Array<double> a(10);
return 0;
}
I got a compilation error , which says:
error C2758: 'Array<T>::m_anchor' : must be initialized in constructor base/member initializer list
It has never happened , what brings me to ask this question:
Must any class-member reference type be initialized in the constructor initialization list?
If so, why? Is that related somehow to the fact that a reference type can never be reassigned?
Are there more types that must be initialized in constructor initialization list?
Does any class-member reference type must be initialized in the constructor initialization list?
Yes.
If so, why? Is that related somehow to the fact the a reference type can never be reassigned?
That's part of the reason. The other part is because a reference must be initialized, and it has no default constructor.
Are there more types that must be initialized in constructor initialization list?
Any type that doesn't have an assignment operator (be it copy or move) or default constructor. This obviously includes (but is not limited to) const members as they can't be modified once they've been constructed.
As a rule of thumb, you should (almost) always prefer to initialize your members in the constructor's initialization list: why waste cycles first default-constructing an object and then only assigning to it (if this is even possible), when you could construct it correctly in the first place?
Must any class-member reference type be initialized in the constructor initialization list?
Yes.
Is that related somehow to the fact that a reference type can never be reassigned?
Yes, plus the fact that there is no "null" or default construction for a reference. It is an alias for another object, and it has to be bound to it from the outset. You cannot do this (this is not inside a class definition):
int main()
{
int& iref; // ERROR
int i = 42;
int& iref2 = i; // OK
}
because iref must alias something.
Must any class-member reference type be initialized in the constructor initialization list?
Yes, we should always use initializer list to initialize reference members of a class.
If so, why? Is that related somehow to the fact that a reference type can never be reassigned?
A constructor has two phases namely initialization and computation. So, even if you don't use initializer list for a data member compiler will initialize it with random value. And in in computation phase which typically starts with the '{' of constructor body, if you do an assignment then compiler will complain as the reference member was already intialized. So, your only chance to initialize such member is the constructor initializer list.
Are there more types that must be initialized in constructor initialization list?
Yes, const data members are the other type for which you need an initializer list.
In C++ Templates The Complete Guide in section 5.3 Member Templates it's written:
Note that a template assignment operator doesn't replace the default
assignment operator. For assignments of stacks of the same type, the
default assignment operator is still called.
Is this correct, because when I ran below code:
#include<iostream>
using namespace std;
template<typename T>
class Pair
{
public:
T pair1,pair2;
Pair(T i,T j):pair1(i),pair2(j){}
template<typename T1>Pair<T>& operator=(Pair<T1>&);
};
template<typename T>
template<typename T1>
Pair<T>& Pair<T>::operator=(Pair<T1>& temp)
{
this->pair1 =temp.pair1*10;//At this point
this->pair2=temp.pair2;
return *this;
}
int main()
{
Pair<int>P1(10,20);
Pair<int>P2(1,2);
P2=P1;
cout<<P2.pair1<<' '<<P2.pair2<<endl;
return 1;
}
I got answer 100 20.
It didn't give the default assignment answer.
Is that a typing mistake in C++ Templates the Complete Guide?
C++ Templates: The Complete Guide By David Vandevoorde, Nicolai M.
Josuttis
Publisher : Addison Wesley
Pub Date : November 12, 2002 Pages : 552
The copy assignment operator is indeed implicitly declared and considered by overload resolution.
A user-declared copy assignment operator X::operator= is a
non-static non-template member function of class X [..]. If the class definition does not explicitly
declare a copy assignment operator, one is declared implicitly. [..]
The implicitly-declared copy assignment operator for a class X will
have the form
X& X::operator=(const X&)
if
each direct base class B of X has a copy assignment operator whose parameter is of type const B&, const volatile B& or B, and
for all the non-static data members of X that are of a class type M (or array thereof), each such class type has a copy assignment
operator whose parameter is of type const M&, const volatile M& or
M.
Otherwise, [..]
As you can see the implicitly-declared copy assignment operator for Pair<int> has one parameter of type Pair<int> const& - note the const in particular! Overload resolution favours non-const references over const ones if both can be bound to the argument, [over.ics.rank]/3:
Two implicit conversion sequences of the same form are
indistinguishable conversion sequences unless one of the following
rules applies:
—
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if
[..]
S1 and S2 are reference bindings (8.5.3), and the types to which the
references refer are the same type except for top-level cv-qualifiers,
and the type to which the reference initialized by S2 refers is more
cv-qualified than the type to which the reference initialized by S1
refers.
The specialization of the template lacks a const in the reference parameter, thus it's a better match and is selected.
The default assignment operator accepts the argument as a const reference: http://en.cppreference.com/w/cpp/language/as_operator.
You have defined a version without const, and your version is better in the context of overload resolution (no conversion required).
Try with the following change:
int main()
{
Pair<int>P1(10,20);
Pair<int>P2(1,2);
const Pair<int>& x = P1;
P2=x;
cout<<P2.pair1<<' '<<P2.pair2<<endl;
return 1;
}
to see the expected result.
I read this in the book:
The synthesized copy-assignment operator is defined as deleted if a
member has a deleted or inaccessible copy-assignment operator, or if
the class has a const or reference member.
And why we can not use reference type?
you're talking about default constructor (and not reassignment or copy constructor).
const member whose type does not explicitly define a default constructor
It forbids the default constructor, else you will have an uninitialized const value (so useless). (if it was not const, the fact that it was uninitialized is not a problem, we may assign it later).
a reference member that does not have an in-class initializer
It is forbidden also, as reference is similar to a non null const pointer.
struct NoDefaultConstructor
{
// No default constructor can be generated.
const int i; // which value to set by default ?
int& r; // reference which object by default?
};
struct InClassInitializerSoDefaultConstruct
{
// default constructor is generated here.
const int i = 42;
int j;
int& r = j;
};
Edit to answer edited Q
For assignment, const value cannot be changed.
and reference are like non null const pointer.
Note that copy constructor doesn't have this restriction because you may (and have to)
initialize const` value.
Having spent the last couple of days experimenting with C++ operator[] methods, I've come across an anomaly that I can't easily explain. The code below implements a simple test string class, which allows the user to access single characters via its subscript "operator" method. As I would like to differentiate between lvalue and rvalue subscript contexts, the "operator" method returns an instance of the custom "CReference" class as opposed to a standard C++ character reference. Whilst the "CReference" "operator char()" and "operator=(char)" methods appear to handle each rvalue and lvalue context respectively, g++ refuses to compile without the presence of an additional "operator=(CReference&)" method as documented below. Whilst the addition of this method appears to placate some kind of compile-time dependency, it is never actually invoked at run-time during the execution of the program.
As someone who thought they had acquired a fundamental understanding of C++ intricacies, this project has certainly proved to be a humbling experience. If anyone could see their way to enlightening me as to what's going on here, I would be eternally grateful. In the meantime, I'm going to have to pull out the C++ books in order to reconcile the void** between what I know and what I think know.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Class for holding a reference to type char, retuned by the "operator[](int)"
// method of class "TestString". Independent methods permit differentiation
// between lvalue and rvalue contexts.
class CReference
{
private:
char& m_characterReference;
public:
// Construct CReference from char&
CReference(char& m_initialiser)
: m_characterReference(m_initialiser) {}
// Invoked when object is referenced in a rvalue char context.
operator char()
{
return m_characterReference;
}
// Invoked when object is referenced in a lvalue char= context.
char operator=(char c)
{
m_characterReference = c;
return c;
}
// NEVER INVOKED, but will not compile without! WHY???
void operator=(CReference &p_assignator){}
};
// Simple string class which permits the manipulation of single characters
// via its "operator[](int)" method.
class TestString
{
private:
char m_content[23];
public:
// Construct string with test content.
TestString()
{
strcpy(m_content, "This is a test object.");
}
// Return pointer to content.
operator const char*()
{
m_content[22] = 0;
return m_content;
}
// Return reference to indexed character.
CReference operator[](int index)
{
return m_content[index];
}
};
int main(int argc, char *argv[])
{
TestString s1;
// Test both lvalue and rvalue subscript access.
s1[0] = s1[1];
// Print test string.
printf("%s\n", (const char*)s1);
return 0;
}
The line s1[0] = s1[1]; causes the compiler to generate an implicit copy assignment operator for CReference if you didn't declare one yourself. This causes an error because your class has a reference member, which can't be copied.
If you added an assignment operator that takes a parameter of type const CReference&, it would get called by the assignment.
In your code, you declared a copy assignment operator of type void operator=(CReference &p_assignator). This can't be called because the righthand side of the assignment is a temporary object, which can't be bound to a non-const reference. However, the act of declaring this operator causes the compiler not to try to define an implicit copy assignment operator, and therefore avoids the previous compilation error. Since this operator can't be called, the compiler goes for the other assignment operator that takes a parameter of type char.
What's happening is that without the definition of operator=(CReference&),there are two possible overloads of operator=: the implicitly-defined operator=(const CReference&) and your operator=(char). When it tries to compiled s1[0] = s1[1], it tries to find the best match for operator=, which is the implicitly-defined operator=(const CReference&). But, that isn't allowed to be implicitly-defined, because CReference contains a reference member, so you get an error.
Conversely, when you do define operator=(CReference&), there's no longer an implicitly-defined assignment operator. Sections 12.8 clauses 9-10 of the C++03 standard state:
9) A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.109)
10) If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly.
The implicitly-declared copy assignment operator for a class X will have the form
X& X::operator=(const X&)
if
— each direct base class B of X has a copy assignment operator whose parameter is of type const B&, const volatile B& or B, and
— for all the nonstatic data members of X that are of a class type M (or array thereof), each such class type
has a copy assignment operator whose parameter is of type const M&, const volatile M& or
M.110)
Otherwise, the implicitly declared copy assignment operator will have the form
X& X::operator=(X&)
So, when it tries to compile s1[0] = s1[1], it has two overload choices: operator=(CReference&) and operator=(char). It choose operator=(char) as the better overload because it can't convert a temporary object (the result of s1[1]) into a non-const reference.
I compiled your program on Ideone by removing the overloaded = and it gives the following error:
prog.cpp: In member function ‘CReference& CReference::operator=(const CReference&)’:
prog.cpp:9: error: non-static reference member ‘char& CReference::m_characterReference’, can't use default assignment operator
prog.cpp: In function ‘int main(int, char**)’:
prog.cpp:70: note: synthesized method ‘CReference& CReference::operator=(const CReference&)’ first required here
As the error points out,
s1[0] = s1[1];
needs the copy assignment operator for CReference class.
You have a reference member variable in your CReference class
char& m_characterReference;
For classes with a reference member, you need to provide your own implementation of = operator as a default = cannot infer what to do with the reference member. So you need to provide your own version of the = operator.
So a little bit of speculation here:
With,
s1[0]=s1[1]
Ideally, for CReference& CReference::operator=(const CReference&) to be invoked the argument should be a const CReference& but the argument s1[1] returns a non constant CReference&, so:
why does the compiler asks for CReference& CReference::operator=(const CReference&) in the code sample link I posted &
Why does the error go away if we provide a = operator with a non constant argument, like CReference& CReference::operator=(CReference&)
As it seems in the absence of an explicit non constant argument copy constructor the compiler does some kind of a optimization and treats the argument of s1[1] as an constant thus asking for the constant argument copy assignment operator.
If a non constant argument copy assignment operator is explicitly provided the compiler doesn't perform its optimization but just uses the one that is already provided.
Instead of returning a CReference, you can return a char&. That would remove the need for the CReference class, which is dangerous for the reasons that I included in my comment above. You can have two forms of the operator[], one returning char and one returning char&.
OK, let's see:
s1[0] = s1[1];
s1[0] returns CReference
s1[1] returns CReference
Of course you need an assignment operator that accepts CReference.
Hence your statement in bold letters:
// NEVER INVOKED, but will not compile without! WHY???
void operator=(CReference &p_assignator){}
is incorrect.
I think your
void operator=(CReference &p_assignator){}
is invoked since
// Test both lvalue and rvalue subscript access.
s1[0] = s1[1];
use the function, as both s1[0] and s1[1] return CReference. and not char.
Also you would like to do something like this
CReference & operator=(CReference &p_assignator){}
to handle
s1[0] = s1[1] = s1[2]; //etc...
UPDATED: a thought
Why we need to provide user-defined copy assignment operator?
Answer: I think the default behavior of the implicit assignment operator is to re-assign the referenced member variable to refer to another location instead of the semantic in m_characterReference = p_assignator.m_characterReference;. Reference variable for us is a syntactic sugar but for the compiler it is a constant pointer. Thus, the default assignment tries to re-assign this constant pointer.
The two functions CReference::operator char() and CReference::operator=(char) do not represent lvalue and rvalue.
As previously mentioned, your main mistake is that you declared the copy assignment operator to return void. Thus, it won't be invoked.
For the compiler to execute "s[0] = s[1];" and based on your code, first, s[x] will be converted to CRefernce anonymous objects. Then the compiler search for operator=(). Since you provide operator=(char), the compiler is "smart" and try to fulfill this assignment. So, it will invoke operator char() first to convert right-hand side (which CReference(s[1]) to char, then invoke operator=(char) function. The expression (s[0] = s[1]) becomes of type char.
You can avoid this conversion by modifying your code as
class CReference
{
private:
char& m_characterReference;
public:
// Construct CReference from char&
CReference(char &m_initialiser)
: m_characterReference(m_initialiser) {}
char& operator = (const CReference &r) {
m_characterReference = r.m_characterReference;
return m_characterReference;
}
};
The assignment operator returns char if you would like to keep the type of "s1[0] = s1[1]" as character. You should not worry about conversion from char to CReference (in case of s1[0] = s1[1] = s1[2]) since 1-arg constructor will handle this conversion.
A simple and clear example that represents lvalue and rvalue is
class Point {
public:
int& x() {return x_value; }
int& y() {return y_value; }
private:
int x_value;
int y_value;
};
int main()
{
Point p1;
p1.x() = 2;
p1.y() = 4;
cout << p1.x() << endl;
cout << p1.y() << endl;
}
I am compiling the code below when the following erro comes up. I am unable to find the reason.
typedef union {
struct {
const int j;
} tag;
} X;
int main(){
return 0;
}
error: member `<`anonymous union>::`<`anonymous struct> `<`anonymous union>::tag with copy assignment operator not allowed in union
This code compiles fines with gcc though. Gives error only with g++.
In order to have a member of a union of some class type T, T's special member functions (the default constructor, copy constructor, copy assignment operator, and destructor) must be trivial. That is, they must be the ones implicitly declared and defined by the compiler.
Your unnamed struct does not have a trivial copy assignment operator (in fact, it doesn't have one at all) because it has a member variable that is const-qualified, which suppresses generation of the implicitly declared copy assignment operator.
The compiler tries to generate an assignment operator for the union itself, and fails because the active field of the union if not known, so it falls back to a bit-for-bit copy of the object. However, it can't do that either, since struct { const int j; } has a non-trivial assignment operator.
See http://gcc.gnu.org/ml/gcc-help/2008-03/msg00051.html.