How doesn't overloading operator() override class constructor - c++

Having the following class with the operator()(string) function and the constructor X(string):
class X
{
public:
string n;
X(string s) : n(s) {}
void operator() (string s)
{
cout << "func";
}
};
Why doesn't X x("a"),y("b"); raise any errors? How does the compiler knows which function to choose?

X x("a"),y("b");
Is equivalent with
X x("a");
X y("b");
So I will focus on
X x("a");
This is a declaration and initialization of the variable x. Only constructors and conversions (*) are considered.
operator() is the function call operator. It is used to call on a object as if it were a function. So it is used on an existing object. E.g.:
X x("asd"); // <-- constructor call
x("yolo"); // <-- operator() call
As a side note I recommend using the brace init syntax {} to initialize objects:
X x{"asd"};
auto x = X{"asd"};
*) As a bonus, here is an example of an user defined conversion operator used:
struct X { };
struct Y
{
operator X() const { return X{}; }
};
auto test()
{
Y y;
X x(y);
// the above line is equivalent with:
X x = static_cast<X>(y.operator X());
}
I used X x(y) to mimic your example, but again, I recommend:
auto test()
{
Y y{};
X x{y};
}
instead

Related

Return unique_ptr member variable from proxy class with operator()

I would like to make a wrapper class (in this case a not null check, but it could be other checks).
Is there any way to extract member variable through operator() that would make it possible to move out the member variable. The use case is a std::unique_ptr<>
This is the use case
#include <memory>
struct S {
int x = 0;
S(int x): x(x) {}
};
template <typename Type>
class NotNull
{
public:
NotNull(Type value): m_value(std::move(value)) {}
operator Type&() // <--------------------------------- this is the question
{
assertIsNotNull();
return m_value;
}
typename std::pointer_traits<Type>::element_type *get() {
return m_value.get();
}
private:
void assertIsNotNull() {}
Type m_value;
};
And this is what needs to work
// Test code
int main() {
{
NotNull<S *> x {new S{10}};
auto y = x; // This works
delete y;
}
{
NotNull<std::shared_ptr<S>> x{std::make_shared<S>(10)};
auto y = x; // This works
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
S* y = x.get(); // This does work, and needs to work as expected
// that is _not_ move the member
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
auto y = std::move(x); // This copies the whole class
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
std::unique_ptr<S> y = std::move(x); // <----------------- This does not work
}
}
The compiler seems to not understand that I want to convert to a unique_ptr inside of the std::move call.
error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = S; _Dp = std::default_delete<S>]'
57 | std::unique_ptr<S> y = std::move(x);
|
Compiler explorer link
Is it possible to use a class as a proxy for std::unique_ptr somehow and get this kind of syntax for moving out member variables.
PS. As you might have guessed I cannot rely on for example gsl::not_null, because this functionality does not exist there to my knowledge. DS.
The compiler understands that you want to convert to a unique_ptr& (i.e Type&) just fine. The problem arises when you assign the result of that conversion to your local unique_ptr object: since its an lvalue reference, the compiler tries to invoke the copy constructor (not the move constructor, which requires an rvalue reference), but since unique_ptr deleted its copy constructor, you get the error.
What you probably wanted to do in that line is to convert to a unique_ptr&& (i.e an rvalue reference). To do that, you can overload your conversion operator based on ref-qualifiers:
operator Type&() & // converts to lvalue ref if invoked on lvalue
{
assertIsNotNull();
return m_value;
}
operator Type&&() && // converts to rvalue ref if invoked on a temporary
{
assertIsNotNull();
return std::move(m_value);
}
This way, the conversion operator will convert to the same type of reference that it was invoked on (i.e lvalue of used from a normal variable, and rvalue if used on a temporary or moved object).
You can use reference qualifiers to make separate versions of a member function depending on whether the object is an lvalue or rvalue reference, like so:
#include <memory>
#include <iostream>
struct S {
int x = 0;
S(int x): x(x) {}
};
template <typename Type>
class NotNull
{
public:
NotNull(Type value): m_value(std::move(value)) {}
operator Type&()
{
assertIsNotNull();
return m_value;
}
auto get() &
{
std::cout << "lvalue \n";
return m_value.get();
}
auto get() &&
{
std::cout << "rvalue \n";
auto retval = m_value.get();
m_value.release();
return retval;
}
private:
void assertIsNotNull() {}
Type m_value;
};
int main()
{
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
S* y = x.get(); // This does work, and needs to work as expected
// that is _not_ move the member
}
{
NotNull<std::unique_ptr<S>> x{std::make_unique<S>(10)};
std::unique_ptr<S> y = std::unique_ptr<S>(std::move(x).get()); // Is this what you want?
}
}

Using a custom constructor as a template function

I have template function change that takes a function that takes int and returns an object of type A. So I thought I can use the constructor of A
class A {
int y;
public:
explicit A(int y) : y(2 * y) {
}
};
class B {
A x;
public:
B(int x) : x(x) {
}
template<typename F>
void change(int y, F func) {
x = func(y);
}
};
int main(void) {
B b(7);
b.change(88, A()); // << here
return 0;
}
But the compiler says no matching function for call to ‘A::A()’
How can I make it works?
You can't pass a constructor as a parameter like you are attempting. The C++ standard is very strict on not allowing the memory address of a constructor to be taken.
When you call change(88, A()), you are actually constructing a temp A object (which the compiler should not allow since A does not have a default constructor) and then you are passing that object to the parameter of change(). The compiler is correct to complain, since A does not define an operator()(int) to satisfy the call to func(y) when called in an A object.
To make this work, you need to create a separate function that constructs the A object, and then pass that function to change(), eg:
A createA(int y)
{
return A(y);
}
int main(void) {
B b(7);
b.change(88, createA);
return 0;
}

Error with implicit copy constructor in struct initialization

I have come up with a problem that I am unable to explain in light of my knowledge of C++.
I have a class that takes a constant reference to a struct of type X but when I pass an argument of type X (which i previously initialized) I get an error from the compiler saying it can't convert to the first member of a member of X. This doesn't make any sense to me.
The error is similar in clang and g++ which leads me to think I am missing something egregious.
error: no viable conversion from 'const Foo::X' to 'Foo::FooTypes'
Why is it trying to convert from X to FooTypes which is the first member of Z?
class Foo {
public:
enum FooTypes {
JPEG
};
struct Z {
FooTypes type;
int a;
};
struct X {
Z caps;
double host_address;
};
Foo(const X& x);
private:
const X x;
};
Foo::Foo(const Foo::X& x) :
x{x} {
}
int main() {
Foo::X new_x = {
{Foo::JPEG, 1}, 1.58 };
Foo *s = new Foo(new_x);
delete s;
return 0;
}
You should use round brackets instead of curly brackets:
...
const X m_x;
};
Foo::Foo(const Foo::X& x) :
m_x(x) {
}
...
here is list initialization:
...
m_x{x.caps, x.host_address}
...
Edit
#PauloNeves
I just found Bjarne Stroustrup's document http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf, which contains next definition:
The general idea of "initializer lists" (as discussed for many years
in EWG) is to allow the use of a brace-enclosed list of expressions in
all contexts that allow initializers. The following list is lifted
from N2532:
Variable initialization; e.g., X x {v};
Initialization of a temporary; e.g, X{v}
Explicit type conversion; e.g. x X{v};
Free store allocation; e.g. p new X{v}
Return value; e.g., X f(){ /* ... */ return {v}; }
Argument passing; e.g., void f(X); /* ... */ f({v});
Base initialization; e.g., Y::Y(v) : X{v} { /* ... */ };
Member initialization; e.g., Y::Y(v) : mx{v} { X mx; /* ... */ };
I think that described Member initialization is your case. So as for me it looks like g++4.9 defect.

C++ constructor of an object which is part of another object

That is not a duplicate. Please read carefully. There are two variables x (of type int and X) and one member is actually declared private, which is used in a constructor. It is about understanding the constructor process in this very specific case.
I am doing a C++ course and I understand the following example that was given. It is about constructors.
#include <iostream>
using namespace std;
class Element {
int value;
public:
Element(int val) {
value = val;
cout << "Element(" << val << ") constructed!" << endl;
}
int Get(void) {
return value;
}
void Put(int val) {
value = val;
}
};
class Collection {
Element el1, el2;
public:
Collection(void) : el2(2), el1(1) {
cout << "Collection constructed!" << endl;
}
int Get(int elno) {
return elno == 1 ? el1.Get() : el2.Get();
}
int Put(int elno, int val) {
if (elno == 1) el1.Put(val);
else el2.Put(val);
}
};
int main(void) {
Collection coll;
return 0;
}
Then they mentioned the following
...
We should also add that there is the following alternation for that
case: when the constructor is divided between the declaration and the
definition, the list of alternative constructors should be associated
with the definition, not the declaration.
It means that the following snippet is correct:
class X {
public:
X(int x) { };
};
class Y {
X x;
public:
Y(int x);
};
Y::Y(int x) : x(1) { };
Can someone explain? Is it really correct? And if yes, how to interpret that? Y has a one-parameter constructor, but no value is passed. x(1) is probably the constructor for the field X x in Y. Is the value of 1 (of x(1)) then passed to Y(int x) automatically although it is declared private in Y?
In the second code snippet, there is no construction actually going on—it's just a definition of the classes and constructors. The only "special" thing about it is that the body of Y's constructor is defined outside the class; it could be in a different file, for example. In this case, it's no different from putting the body directly into the class1:
class Y {
X x;
public:
Y(int x) : x(1) {}
};
When this constructor of Y is invoked, it constructs the member variable x by passing 1 to the X constructor taking int. The constructor of Y doesn't do anything else, i.e. it ignores its own parameter.
The syntax Y::Y used in the original code snippet is standard syntax for defining a member function outside of the class definition. For a non-constructor, it would look like this:
class Foo
{
int bar() const;
};
int Foo::bar() const
{
return 42;
}
1 With the slight difference that when put directly into the class definition, the function is implicitly inline (can be present in more than one translation unit).

Return type of a constructor in c++

I am learning c++, and I came across following code segment:
class X
{
int i;
public:
X(int ii = 0);
void modify();
};
X::X(int ii)
{ i = ii; }
void X::modify()
{ i++; }
X f5()
{ return X(); }
const X f6()
{ return X(); }
void f7(X& x) // Pass by non-const reference
{ x.modify(); }
int main()
{
f5() = X(1);
f5().modify();
}
I am stuck particularly in this segment-
X f5()
{ return X(); }
Does this part of code return an object of type x by value? Can a constructor return an object?
Does this part of code returns an object of type x by value?
Yes, it creates and value-initialises a temporary object of type X (by calling the constructor with the default value of zero) and returns that.
can a constructor can return an object?
No, that doesn't make any sense. But an conversion expression like X() does.
f5 is just function, not constructor. And it returns constructed object of type X.
simple, you misunderstand the constructor of the class and a function. your class class X have a constructor and it is X(int ii = 0);.
X f5() is not a constructor. Clearly understand that first.
Constructor of a class should have the same name as class and have no a return value. It does,t make any sense. (read further about class and constructor)
X f5() {
return X();
}
This is a function which returns a X type of object. it returns the X().
X() creates a value-initialized temporary object of type X.