The following code only works when the copy constructor is available.
When I add print statements (via std::cout) and make the copy constructor available it is not used (I assume there is so compiler trick happening to remove the unnecessary copy).
But in both the output operator << and the function plop() below (where I create a temporary object) I don't see the need for the copy constructor. Can somebody explain why the language needs it when I am passing everything by const reference (or what I am doing wrong).
#include <iostream>
class N
{
public:
N(int) {}
private:
N(N const&);
};
std::ostream& operator<<(std::ostream& str,N const& data)
{
return str << "N\n";
}
void plop(std::ostream& str,N const& data)
{
str << "N\n";
}
int main()
{
std::cout << N(1); // Needs copy constructor (line 25)
plop(std::cout,N(1)); // Needs copy constructor
N a(5);
std::cout << a;
plop(std::cout,a);
}
Compiler:
[Alpha:~/X] myork% g++ -v
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5646~6/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/include/c++/4.2.1 --program-prefix=i686-apple-darwin10- --host=x86_64-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5646)
[Alpha:~/X] myork% g++ t.cpp
t.cpp: In function ‘int main()’:
t.cpp:10: error: ‘N::N(const N&)’ is private
t.cpp:25: error: within this context
t.cpp:10: error: ‘N::N(const N&)’ is private
t.cpp:26: error: within this context
This is a simplified version of some real code.
In the real code I have a class that contains a std::auto_ptr. This means that a copy constructor that takes a const reference is not valid (without some work) and I was getting an error indicating that the copy constructor was not available because of it:
Change the class too:
class N
{
public:
N(int) {}
private:
std::auto_ptr<int> data;
};
The error is then:
t.cpp:25: error: no matching function for call to ‘N::N(N)’
From http://gcc.gnu.org/gcc-3.4/changes.html
When binding an rvalue of class type
to a reference, the copy constructor
of the class must be accessible. For
instance, consider the following code:
class A
{
public:
A();
private:
A(const A&); // private copy ctor
};
A makeA(void);
void foo(const A&);
void bar(void)
{
foo(A()); // error, copy ctor is not accessible
foo(makeA()); // error, copy ctor is not accessible
A a1;
foo(a1); // OK, a1 is a lvalue
}
This might be surprising at first
sight, especially since most popular
compilers do not correctly implement
this rule (further details).
This will be fixed in C++1x by Core Issue 391.
The applicable parts of the standard here are §8.5.3/5, which covers initialization of references and §3.10/6, which tells what's an rvalue and what's an lvalue (not always obvious in C++).
In this case, you initialization expression is: "N(1)", so you're explicitly creating an object using functional notation. According to 3.10/6, that expression is an rvalue.
Then we have to walk through the rules in 8.5.3/5 in order, and use the first that applies. The first possibility is if the expression represents an lvalue, or can be implicitly converted to an lvalue. Your expression is an rvalue, and implicit conversion to an lvalue would require a conversion function that returns a reference, which doesn't seem to exist in this case, so that doesn't seem to apply.
The next rule says the reference must be to a const T (which is the case here). In this case, the expression is an rvalue of class type and is reference-compatible with the reference (i.e. the reference is to the same class, or a base of the class). That means the bullet at the bottom of page 151 (179 of the C++ 2003 PDF) seems to apply. In this case, the compiler is allowed to either bind the reference directly to the object representing the rvalue, OR create a temporary copy of the rvalue, and bind to that temporary copy.
Either way, however, the standard explicitly requires that: "The constructor that would be used to make the copy shall be callable whether or not the copy is actually done."
As such, I believe that gcc is right to give an error message, and the others are technically wrong to accept the code. I simplified your code a bit to the following:
class N {
public:
N(int) {}
private:
N(N const&);
};
void plop(N const& data) { }
int main() {
plop(N(1));
}
When invoked with "--A" (strict errors mode), Comeau gives the following error message:
"plop.cpp", line 12: error: "N::N(const N &)", required for copy that was
eliminated, is inaccessible
plop(N(1));
^
Likewise, when invoked with "/Za" (its "ANSI conforming" mode), VC++ 9 gives:
plop.cpp
plop.cpp(12) : error C2248: 'N::N' : cannot access private member declared in class 'N'
plop.cpp(6) : see declaration of 'N::N'
plop.cpp(2) : see declaration of 'N'
while checking that elided copy-constructor 'N::N(const N &)' is callable
plop.cpp(6) : see declaration of 'N::N'
when converting from 'N' to 'const N &'
My guess is that most of the other compilers do roughly the same. Since they optimize out the call to the copy constructor, they don't normally require that it exist or be accessible. When you ask them to conform to the standard as accurately as they can, they give the error message, because it's technically required even though they don't use it.
Related
After many years of using C++ I realized a quirk in the syntax when using custom classes.
Despite being the correct language behavior it allows to create very misleading interfaces.
Example here:
class complex_arg {
double r_;
double phi_;
public:
std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};
int main() {
complex_arg ca;
ca.value() = std::complex<double>(1000., 0.); // accepted by the compiler !?
assert( ca.value() != std::complex<double>(1000., 0.) ); // what !?
}
https://godbolt.org/z/Y5Pcjsc8d
What can be done to the class definition to prevent this behavior?
(Or at the least flag the user of the clas that the 3rd line is not really doing any assignment.)
I see only one way out, but it requires modifying the class and it doesn't scale well (to large classes that can be moved).
const std::complex<double> value() const;
I also tried [[nodiscard]] value() but it didn't help.
As a last resort, maybe something can be done to the returned type std::complex<double> ? (that is, assuming one is in control of that class)
Note that I understand that sometimes one might need to do (optimized) assign to a newly obtained value and passe it to yet another function f( ca.value() = bla ). I am not questioning this usage per se (although it is quite confusing as well); I have the problem mostly with ca.value() = bla; as a standalone statement that doesn't do what it looks.
Ordinarily we can call a member function on an object regardless of whether that object's value category is an lvalue or rvalue.
What can be done to the class definition to prevent this behavior?
Prior to modern C++ there was no way prevent this usage. But since C++11 we can ref-qualify a member function to do what you ask as shown below.
From member functions:
During overload resolution, non-static member function with a cv-qualifier sequence of class X is treated as follows:
no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument
lvalue ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X
rvalue ref-qualifier: the implicit object parameter has type rvalue reference to cv-qualified X
This allows us to do what you ask for a custom managed class. In particular, we can & qualify the copy assignment operator.
struct C
{
C(int)
{
std::cout<<"converting ctor called"<<std::endl;
}
C(){
std::cout<<"default ctor called"<<std::endl;
}
C(const C&)
{
std::cout<<"copy ctor called"<<std::endl;
}
//-------------------------v------>added this ref-qualifier
C& operator=(const C&) &
{
std::cout<<"copy assignment operator called"<<std::endl;;
return *this;
}
};
C func()
{
C temp;
return temp;
}
int main()
{
//---------v---------> won't work because assignment operator is & qualified
func() = 4;
}
I've been trying to produce a class with a member that is an std::array of a non-copyable type, that I need to initialize in the constructor. I had thought, referring to answers on this SO question, that the following would work:
#include <array>
#include <utility>
class Foo {
public:
Foo() {}
Foo(const Foo& rhs) = delete;
Foo(Foo&& rhs) = delete;
};
template<size_t BufferSize>
class FooBuffer {
public:
template<size_t... Is>
FooBuffer(std::index_sequence<Is...>)
: _buffer({{(static_cast<void>(Is), Foo{})...}})
{
}
FooBuffer() : FooBuffer(std::make_index_sequence<BufferSize>{}) {}
private:
std::array<Foo,BufferSize> _buffer;
};
using namespace std;
int main(int, char **) {
FooBuffer<10> foo;
}
And so it does, from GCC version 7.1.0 onwards with the C++17 or C++17(GNU) flags, according to Wandbox:
link to working compilation under GCC 7.1.0
However, though guaranteed copy elision support is listed for Clang++ from version 4 onwards, I can find no version that accepts the above code, up through the 9.0 or the current HEAD:
link to compiler error under Clang
The error that is yielded relates to the non-copy-constructibility of the array _buffer:
prog.cc:18:5: error: call to implicitly-deleted copy constructor of 'std::array<Foo, 10UL>'
: _buffer({{(static_cast<void>(Is), Foo{})...}})
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
prog.cc:22:16: note: in instantiation of function template specialization 'FooBuffer<10>::FooBuffer<0, 1, 2, 3, 4, 5, 6, 7, 8, 9>' requested here
FooBuffer() : FooBuffer(std::make_index_sequence<BufferSize>{}) {}
^
prog.cc:32:16: note: in instantiation of member function 'FooBuffer<10>::FooBuffer' requested here
FooBuffer<10> foo;
^
/opt/wandbox/clang-head/include/c++/v1/array:143:9: note: copy constructor of 'array<Foo, 10>' is implicitly deleted because field '__elems_' has a deleted copy constructor
_Tp __elems_[_Size];
^
prog.cc:8:2: note: 'Foo' has been explicitly marked deleted here
Foo(const Foo& rhs) = delete;
^
1 error generated.
Is this a disagreement on the implementation of guaranteed copy elision between the compilers? Or, I've seen indications here on SO that guaranteed copy elision cannot be relied on and is entire optional for the compiler. Does that apply here, or only in cases where there is a copy constructor and GCE is not required for correct code?
When you do
_buffer({{(static_cast<void>(Is), Foo{})...}})
The {(static_cast<void>(Is), Foo{})...} part builds a braced-init-list for an object. The outer {} is the braces for creating a the object the braced-init-list refers to. Since none of these list has a type the compiler has to enumerate the constructors of _buffer to find out what to call. When the compiler does this all that is found are the implicitly generated copy and move constructors. Since Foo is not copy/move-able those are implicitly deleted which means there is no constructor you can call.
If you switch to
_buffer{(static_cast<void>(Is), Foo{})...}
Then you have direct initialization of _buffer and this is guaranteed to work since the Foo{} prvalues are not copied but created directly in place.
You could also switch to using
_buffer(std::array<Foo,BufferSize>{{(static_cast<void>(Is), Foo{})...}})
and this will work because now you do have a prvalue of type std::array<Foo,BufferSize> and instead of being copied it is directly initialized in _buffer.
The pertinent standardese is found in [dcl.init]/17
If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object.
emphasis mine
Since a braced-init-list has no type, it does not qualify for the above bullet point so no guaranteed copy elision.
T(args...) is direct initialisation. For direct initialisation of a class type, copy elision is guaranteed only when the argument is of that class type. But in your example, the argument is a braced-init-list.
GCC's failure to diagnose the issue is a violation of the standard.
This can be fixed by explictly creating a temporary, that will not be materialised (as perverse as it may seem):
: _buffer(std::array{(static_cast<void>(Is), Foo{})...})
Or simply by using list initialisation:
_buffer{(static_cast<void>(Is), Foo{})...}
Using Clang 3.7 on windows platform
See following code:
class A1
{
public:
A1(char* name){}
virtual ~A1() {}
private:
A1(const A1&) {}
};
class B1 : public A1
{
public:
B1(): A1(""){}
};
I get the following error:
MyFile(31): 8: error: base class 'A1' has private copy constructor
B1(): A1(""){}
^
MyFile(25): 2: note: declared private here
A1(const A1&) {}
^
Making A1 copy constructor public, eliminates the error!
What happened here?
Note: that by changing (as I should)
A1(const char* name)
I get no errors and all compile as expected
I imagine that this is just an artefact of how the diagnostics are generated.
First, lookup attempts to find a constructor to match your "call" (it's not a call but whatever)
The ctor taking char* doesn't match, as you know
The only other candidate is private
The compiler tries to see whether it can instantiate a temporary A1 from your "" argument to make that work
In doing so, again all it can find is private constructors
The compiler decides to complain about that before doing anything else
One might argue that this is a quality of implementation issue.
Amusingly, GCC 6.1.0 (even in pedantic C++14 mode) compiles your code as originally written, spitting out only a warning for the broken literal conversion.
You cannot call the constructor A1(char* name) using a string literal, becase a string literal is not convertible to char* (such deprecated conversion did exist prior to c++11). Or rather, a program that does call the constructor is ill-formed, and the implementation is allowed to refuse to compile.
As such, the overload resolution looks for other alternatives. The only other potential alternative that has the same number of arguments, is the copy constructor.
For whatever reason, clang appears to prefer the implicit conversion from string literal, to A1, thereby creating a temporary, that could be used for copy-initialization, over using the direct construction from the literal. This behaviour leads to the confusing compilation error.
Both alternatives are ill-formed, and clang appropriately warns about it: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings]. The program does compile, if you set the standard mode to older than c++11 (in which case the program would be well-formed, even though it does use a deprecated conversion). Interestingly, if we disallow the conversion, then the program compiles even in the current standard mode:
class A1
{
public:
explicit A1(char* name){} // note the explicit
virtual ~A1() {}
private:
A1(const A1&) {}
};
G++ behaves differently and your program compiles fine (with the appropriate warning of course). Both compilers appear to comply to the standard in this regard.
Moral of the story: Always read the warnings as well. In this case, the warning was perfectly clear, and easy to solve, while the same bug indirectly caused an error that was not helpful in the solving of the bug.
I was making a thin derived class with a forwarding constructor. (Bear with me, I must use GCC 4.7.2, which lacks inherited constructors).
On the first try, I forgot to add the explicit keyword and got an error. Could someone explain exactly why this particular error occurs? I'm having trouble figuring out the sequence of events.
#include <memory>
template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
template<typename...Args>
/*explicit*/ shared_ptr(Args &&... args)
: std::shared_ptr<T>(std::forward<Args>(args)...)
{}
};
struct A {};
struct ConvertsToPtr
{
shared_ptr<A> ptr = shared_ptr<A>(new A());
operator shared_ptr<A> const &() const { return ptr; }
};
int main()
{
shared_ptr<A> ptr;
ptr = ConvertsToPtr(); // error here
return 0;
}
The error:
test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)
This is also the case with g++ 4.8.4 with the following:
g++ -g -pedantic --std=c++11 -o test main.cpp
The VS2015 settings are all defaulted.
The problem is that the compiler tries to convert a temporary returned by ConvertsToPtr() to a shared_ptr object. When the compiler is used with explicit keyword, then this conversion never occurs using the constructor. However, while examining with gdb it appears that instead it is using the shared_ptr<A> const &() conversion function to match the appropriate Type. This conversion then returns a const shared_ptr & which has no ambiguity when invoking the assignment operator (this is also match the findings of wojciech Frohmberg).
However, if the explicit is omitted, then an object of shared_ptr is returned. this can be matched either to rvalue version of the assignment operator or the const lvalue version.
According to N4296, Table-11, then we have, after the construction with the conversion constructor, a rvalue of shared_ptr object. However the overload resolution finds two matches, which both ranks under Exact Match (the rvalue version is Identity matching while the other is under Qualification matching).
I did check also on VS2015 and like stated in the comments, it works. But using some cout debugging one can see that the const lvalue assignment rvalue is prioritized over the rvalue const lvalue refrence version counterpart.
EDIT: I looked a little deeper in the standard and add the modification. the deleted text regarding the results VS2015 was wrong, because I didn't define both assignments. When both of assignments were declared it does prefer the rvalue.
I assume that the VS compiler distinct the Identity from the Qualification matching in ranking. However as I conclude, it is the VS compiler that is buggy. the g++ compilers obeys the given standard. However since GCC 5.0 Does work as Visual studio, The possibility of compiler bug is slim, so I would be happy to see another experts insights.
EDIT: In 13.3.3.2 one of the draw breakers, after the better ranking I wrote about it, is:
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a non-static member function declared
without a ref-qualifier, and S1 binds an rvalue reference to an rvalue
and S2 binds an lvalue reference.
There is an example attached showing that a given rvalue (not rvalue reference) is supposed to match a const int && over const int &. Therefore I guess, it is safe to assume that it is relevant to our case, even if we have && type and not const && type. I guess after all that GCC 4.7,4.8 is buggy after all.
The following code does not compile on Visual C++ 2008 nor 2010:
#include <memory>
struct A {};
std::auto_ptr<A> foo() { return std::auto_ptr<A>(new A); }
const std::auto_ptr<A> bar() { return std::auto_ptr<A>(new A); }
int main()
{
const std::auto_ptr<A> & a = foo(); // most important const
const std::auto_ptr<A> & b = bar(); // error C2558:
// class 'std::auto_ptr<_Ty>' :
// no copy constructor available or copy
// constructor is declared 'explicit'
bar(); // No error?
}
I expected the "most important const" to apply to the variable "b", and yet, it does not compile, and for some reason, the compiler asks for a copy constructor (which surprises me as there should be no copy involved here). The standalone call to bar() works fine, which means, I guess, it is really the initialization of b that is the problem.
Is this a compiler bug, or a genuine compilation error described in the standard?
(perhaps it was forbidden in C++98 and authorized in C++11?)
Note: It does compile on Visual C++ 2012, gcc 4.6, and on Solaris CC (of all compilers...), but not gcc 3.4, nor XL C)
In C++03 and C++98, when binding a const reference to an rvalue (such as a function returning by value), the implementation may bind the reference directly to the rvalue or it may make a copy of the rvalue and bind the reference to that copy. As auto_ptr's copy constructor takes a non-const reference, this second choice will only work if the rvalue returned is not const qualified but the compiler is still allowed to attempt this, even if it won't work.
In C++11, these extra copies are not allowed and the implementation must bind directly to the rvalue if a conversion isn't required.
See also here.
Pre C++11, at least, the standard required an object to be
copyable in this context. In the end, the semantics of:
T const& t = f();
, where f returns a T by value, is:
T tmp = f();
T const& t = tmp;
Which requires a copy constructor.
In the case of std::auto_ptr, the problem that you're seeing
is that the copy constructor is defined to take a non-const
reference, which means that you cannot copy a temporary. Some
compilers (e.g. Microsoft) don't enforce this, which means that
your code may work with them, but it is fundamentally illegal.
The real question is why you are using references here. You
need a local variable one way or the other; the reference only
introduces an additional layer of indirection.