C++ initialize a unique_ptr with int 0 problem - c++

environment: C++11/14 and MacOS Clion
First, I know it is better to construct a unique_ptr with nullptr rather than with int: 0, but I just wonder what causes the following two compilation results:
// compile just fine
class MyClass {};
void MyFunc(unique_ptr<MyClass>) {
}
int main() {
MyFunc(0);
return 0;
}
// compile error
class MyClass {};
void MyFunc(unique_ptr<MyClass>) {
}
int main() {
MyFunc(int(0));
return 0;
}
the latter one with error:
note: candidate function not viable: no known conversion from 'int' to 'unique_ptr' for 1st argument
After examining unique_ptr's constructor, I find the following a canditate:
constexpr unique_ptr( nullptr_t ) noexcept;
So I try further:
// good
int main() {
nullptr_t mynullptr(0);
return 0;
}
on the other hand:
// error
int main() {
nullptr_t mynullptr(int(0));
return 0;
}
with message:
error: cannot initialize a variable of type 'std::nullptr_t' (aka 'nullptr_t') with an rvalue of type 'int'
nullptr_t mynullptr(int(0));
So is it because the initialization of nullptr that leads to the compile error?

Credit on the comment of M.M:
literal 0 may be converted to nullptr ; other integer expressions may not (even if they are constant and have value zero)

Related

Why isn't shared_ptr implicitly converted to boolean when returning it in a function?

The following won't compile:
#include <memory>
class A;
bool f() {
std::shared_ptr<A> a;
return a;
}
int main()
{
f();
return 0;
}
and fails with:
Compilation failed due to following error(s).main.cpp: In function ‘bool f()’:
main.cpp:13:12: error: cannot convert ‘std::shared_ptr’ to ‘bool’ in return
return a;
What could be the reasoning of the standard (I presume) not allowing an implicit conversion here?
Because the user-defined operator for converting an std::shared_ptr to bool is explicit:
explicit operator bool() const noexcept;
Note that an implicit conversion to bool in the condition of an if statement – among others – still happens even with an explicit user-defined conversion operator to bool :
std::shared_ptr<int> ptr;
if (ptr) { // <-- implicit conversion to bool
}
That is, you don't need to write static_cast<bool>(ptr) in the condition of an if statement.

Why does this constructor initialiser list which initialises a C-array compile?

I very rarely use C-arrays myself but I came up with the following bit of code while trying to answer someone else's question:
#include <cstddef>
template <std::size_t n>
class A {
public:
explicit A(const int (&arr) [n]) : arr_(arr) { }
private:
const int arr_ [n];
};
int main(int, char**) {
const int arr [3] = { 1, 2, 3 };
A<3> a (arr); // (1)
return 0;
}
It doesn't compile and I don't expect it to:
<source>:6:38: error: array initializer must be an initializer list
explicit A(const int (&arr) [n]) : arr_(arr) { }
^
<source>:14:8: note: in instantiation of member function 'A<3>::A' requested here
A<3> a (arr);
I don't expect it to compile for the same reason the following doesn't compile:
int main(int, char**) {
const int arr_a [3] = { 1, 2, 3 };
const int arr_b [3] = arr_a;
return 0;
}
Here's the compilation error:
<source>:3:13: error: array initializer must be an initializer list
const int arr_b [3] = arr_a;
^
But if you in the first code block comment out the line marked (1) it compiles. As if, as I understand it, there's some way to call A::A(const int (&) [n]) that's valid. In my mind, even trying to initialise arr_ with arr is wrong.
Why does the first code block compile if you comment out the line marked (1)? Is there a way to call A::A(const int (&) [n]) that's valid?
It doesn't compile. Literally. You're not using the template, hence the compiler doesn't even look at it.
The error message is actually a bit longer than you quoted. It also answers your question:
test.cc:6:38: error: array initializer must be an initializer list
explicit A(const int (&arr) [n]) : arr_(arr) { }
^
test.cc:14:8: note: in instantiation of member function 'A<3>::A' requested here
A<3> a (arr); // (1)
^
So, only if the template is instantiated will the compiler actually attempt to compile the faulty code and discover its flaw. Hence, no error is produced if you comment out that line.
Actually, compilation of templates is a bit more complicated, being split into two phases. In the first phase, the template is essentially checked for typographical correctness, while the actual compilation into code can only be done in the second phase, when the template is instantiated.

Why is the = operator deleted, and how can I work around it?

I'm trying to set one of the elements of an array to be a different object. But, the compiler is deleting the = operator. Why is it doing that here, exactly? And how can I work around it?
Example code:
struct IntContainer
{
IntContainer(const int value) :
value(value)
{
}
IntContainer() :
IntContainer(0)
{
}
const int value;
};
int main(int argc, char** argv)
{
IntContainer intContainers[3];
IntContainer newIntContainer(420);
intContainers[0] = newIntContainer; // <-- Causes compiler error
return 0;
}
The compiler error I'm getting when compiling this snippet is:
main.cpp: In function 'int main(int, char**)':
main.cpp:23:24: error: use of deleted function 'IntContainer& IntContainer::operator=(const IntContainer&)'
intContainers[0] = newIntContainer; // <-- Causes compiler error:
^~~~~~~~~~~~~~~
main.cpp:2:8: note: 'IntContainer& IntContainer::operator=(const IntContainer&)' is implicitly deleted because the default definition would be ill-formed:
struct IntContainer
^~~~~~~~~~~~
main.cpp:2:8: error: non-static const member 'const int IntContainer::value', can't use default assignment operator
The compiler generally gives you operator= and the copy constructor for free, but when a class contains a const member, it makes no sense to generate operator= because you can't perform an assignment to the const member.
You could write your own, but you still wouldn't be able to assign values to the const member.

Not allowed to return a function from a function. How could I?

8.3.5/8 Functions [dcl.fct] says
[...] Functions shall not have a return type of
type array or function, although they may have a return type of type pointer or reference to such things. [...]
Why so explicit of a rule? Is there some syntax that would even allow returning a function as opposed to a function pointer?
Am I miss-interpreting the quote?
typedef void (*fp)();
void foo(){}
fp goo()
{
return foo; //automatically converted to function pointer
}
This is quite a contrived example of a function trying to return a function:
void foo() { }
template<typename T>
T f() { return foo; }
int main(){
f<decltype(foo)>();
}
This is the error I get from Clang 3.2:
Compilation finished with errors:
source.cpp:7:5: error: no matching function for call to 'f'
f<decltype(foo)>();
^~~~~~~~~~~~~~~~
source.cpp:4:3: note: candidate template ignored: substitution failure
[with T = void ()]: function cannot return function type 'void ()'
T f() { return foo; }
~ ^
1 error generated.
Is there some syntax that would even allow returning a function as opposed to a function pointer?
A syntax? Sure there is:
using fun = int (int);
fun function_that_returns_a_function();
That doesn’t compile because the rule in §8.3.5/8 forbids it. I don’t know why the rule specifically exists – but consider that the type “function” doesn’t have any size so you cannot create objects of function type in C++.
I know this probably does not answer your question completely but it does so partially
You can return a function from another function (that's what lambdas are)
std::function<int (int)> retLambda() {
return [](int x) { return x; };
}

creating a type which can be initialized with assignment, but is not copyable

I am looking to create a type which can be initialized with assignment from another type, but cannot be copied. The idea is similar to that of a scoped smart pointer in that I want the object of this type to own a resource for its lifetime, but I also want to be able to use assignment syntax. So in synopsis, this is what I want:
T x = new U; // allowed
T y(new U); // allowed
T z = x; // not allowed
T x2(x) // not allowed
This is what I have tried so far...
#include <boost/noncopyable.hpp>
class U {};
class T : boost::noncopyable {
public:
T(U *p) : p_(p) {
}
~T() {
delete p_;
}
operator bool() const { return p_ != 0; }
private:
U *p_;
};
int main() {
T x = new U; // allowed
T y(new U); // allowed
//T z = x; // not allowed
//T x2(x) // not allowed
}
which unfortunately results in an error:
$ g++ test.cc -o test /usr/include/boost/noncopyable.hpp: In copy
constructor 'T::T(const T&)': /usr/include/boost/noncopyable.hpp:27:7:
error: 'boost::noncopyable_::noncopyable::noncopyable(const
boost::noncopyable_::noncopyable&)' is private test.cc:6:30: error:
within this context test.cc: In function 'int main()': test.cc:20:12:
note: synthesized method 'T::T(const T&)' first required here
NOTE: C++11's move features are a non-option for me, as this must be able to be compiled with a relatively old version of gcc that does not have C++11 support.
Because of the lack of C++11 support, I am not sure there is a "good" solution. But I figured I'd ask.
If my understanding from comments is correct - you want something like that:
U* create_T() { return new U; }
if (T t = create_T())
{
// do something with t
}
The problem here, already mentioned in comments: this syntax T t = u is just calling copy constructor. If u is of type T it is equivalent to: T t(u). If u, like in your example, is of another type convertible to T (here by T::T(U*)) then it is actually this: T t(T(u)). So here you have the copy contructor for which compiler complains.
There is no solution, because this is not valid if syntax:
if (T i(create_T())) {}
However I would not write all of these without good advise ;)
You can forget about problems with copy constructor with this syntax:
if (U* u = create_T()) {
T t(u);
....
}
BTW, std::auto_ptr has the same problem as your T type. Just add explicit to your T(U*) constructor and you will see similarity to auto_ptr:
class U {};
class T : boost::noncopyable {
public:
explicit T(U *p) : p_(p) {
}
~T() {
delete p_;
}
private:
U *p_;
};
int main() {
T x = new U; // allowed
T y(new U); // allowed
std::auto_ptr<U> a = new U;
std::auto_ptr<U> b(new U);
}
Results in:
prog.cpp:25: error: conversion from ‘U*’ to non-scalar type ‘T’ requested
prog.cpp:27: error: conversion from ‘U*’ to non-scalar type ‘std::auto_ptr<U>’ requested
Here my ideone study...