Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is this constructor?

47 views
Skip to first unread message

woodb...@gmail.com

unread,
Oct 14, 2018, 3:45:06 PM10/14/18
to

I saw the following here: https://en.cppreference.com/w/cpp/language/noexcept_spec

struct A{
A(int = (A(5),0))noexcept;
A(const A&)noexcept;
A(A&&)noexcept;
~A();
};

What is the first constructor doing? Thanks.


Brian
Ebenezer Enterprises - Enjoy programming again
http://webEbenezer.net

Öö Tiib

unread,
Oct 14, 2018, 3:56:37 PM10/14/18
to
On Sunday, 14 October 2018 22:45:06 UTC+3, woodb...@gmail.com wrote:
> I saw the following here: https://en.cppreference.com/w/cpp/language/noexcept_spec
>
> struct A{
> A(int = (A(5),0))noexcept;
> A(const A&)noexcept;
> A(A&&)noexcept;
> ~A();
> };
>
> What is the first constructor doing? Thanks.

It is not known what the constructor is doing since definition of it
has not been posted. The default argument to A(int) uses comma
operator to achieve that temporary A with parameter 5 is constructed
first.

Sam

unread,
Oct 14, 2018, 4:28:05 PM10/14/18
to
woodb...@gmail.com writes:

>
> I saw the following here:
> https://en.cppreference.com/w/cpp/language/noexcept_spec
>
> struct A{
> A(int = (A(5),0))noexcept;
> A(const A&)noexcept;
> A(A&&)noexcept;
> ~A();
> };
>
> What is the first constructor doing? Thanks.

The first constructor takes an optional int parameter. If not passed, the
default value of the int parameter is

(A(5), 0)

This expression first construct a temporary A object, this time passing the
first parameter as 5, then this gets destroyed by the comma operator, and
the whole expression evaluates to 0, thus constructing the object with the
int parameter's value set to 0.

So, if this class's constructor is not supplied with a value, a temporary
instance of this class gets constructed using the parameter value of 5, this
temporary instance of this class gets destroyed, then the actual instance
gets constructed with the int value parameter of 0. Why this is done this
way, only the devil knows.

Chris Vine

unread,
Oct 14, 2018, 5:59:01 PM10/14/18
to
On Sun, 14 Oct 2018 16:27:48 -0400
Sam <s...@email-scan.com> wrote:
> woodb...@gmail.com writes:
>
> >
> > I saw the following here:
> > https://en.cppreference.com/w/cpp/language/noexcept_spec
> >
> > struct A{
> > A(int = (A(5),0))noexcept;
> > A(const A&)noexcept;
> > A(A&&)noexcept;
> > ~A();
> > };
> >
> > What is the first constructor doing? Thanks.
>
> The first constructor takes an optional int parameter. If not passed, the
> default value of the int parameter is
>
> (A(5), 0)
>
> This expression first construct a temporary A object, this time passing the
> first parameter as 5, then this gets destroyed by the comma operator, and
> the whole expression evaluates to 0, thus constructing the object with the
> int parameter's value set to 0.

On a minor quibble: the lifetime of the temporary is not destroyed by
the comma operator. According to §12.2/3 of C++14 "Temporary objects
are destroyed as the last step in evaluating the full-expression (1.9)
that (lexically) contains the point where they were created".
According to §1.9/10 "A full-expression is an expression that is not a
subexpression of another expression". This includes the entire
expression (A(5), 0). I think it also includes the lifetime of the
constructor of the A object which is initialized by this expression,
because any temporaries created on evaluating the arguments of a
constructor or other function last during the execution of the
function. This would seem to be confirmed by the code below which
prints:

In constructor with with value 5
In constructor with with value 0
Destroying object with value 5
Destroying object with value 0

#include <iostream>

struct A{
int i_;
A(int i = (A(5),0)) noexcept : i_(i)
{
std::cout << "In constructor with with value " << i << std::endl;
}
A(const A&) noexcept {}
A(A&&) noexcept {}
~A()
{
std::cout << "Destroying object with value " << i_ << std::endl;
}
};

int main ()
{
A a;
}

woodb...@gmail.com

unread,
Oct 14, 2018, 10:29:44 PM10/14/18
to
I guess this is a "garbagio" constructor.


Brian

Chris Vine

unread,
Oct 15, 2018, 5:07:30 AM10/15/18
to
It seems intended as an illustration of how sub-expressions work in
relation to the noexcept keyword.

So far as the comma operator is concerned, I don't find myself using
it very often, but one use is in relation to copy constructors. Say you
have a type T with a member 'x' protected by a mutex 'm'. The lifetime
rules for temporaries mean that the following initialization of 'x' is
atomic because the mutex will be remain locked while the initialization
is taking place (the full expression in which the temporary is created
does not expire until initialization of the new 'x' has completed):

T(const T& t): x((std::lock_guard(t.m), t.x)) {}

This avoids having to first default construct the new 'x' and then
assign to it under the mutex in the constructor.

I was somewhat surprised about this when I first came across it, but if
you add some instrumentation it can be seen that it does actually work.

Fraser Ross

unread,
Oct 15, 2018, 7:03:43 AM10/15/18
to
Implicit conversions from int to A has not been mentioned by anyone. It
is not specifically saying anything about that such as with this
theoretical code:
implicit A(int = (A(5),0))noexcept;
Although there is the keyword explicit there is not implicit. Using
implicit would at least suppress warnings if it existed.

Fraser.

vam...@proekspert.ee

unread,
Oct 15, 2018, 8:17:43 AM10/15/18
to
On Monday, October 15, 2018 at 12:07:30 PM UTC+3, Chris Vine wrote:

> Say you
> have a type T with a member 'x' protected by a mutex 'm'. The lifetime
> rules for temporaries mean that the following initialization of 'x' is
> atomic because the mutex will be remain locked while the initialization
> is taking place (the full expression in which the temporary is created
> does not expire until initialization of the new 'x' has completed):
>
> T(const T& t): x((std::lock_guard(t.m), t.x)) {}
>
> This avoids having to first default construct the new 'x' and then
> assign to it under the mutex in the constructor.

If it is really needed then it can lead to someone naively thinking
that ...

T(const T& t)
: x((std::lock_guard(t.m), t.x))
, y((std::lock_guard(t.m), t.y))
{}

... is also thread-safe and typical surprises about how that T with
broken invariant was constructed. ;-)

Chris Vine

unread,
Oct 15, 2018, 11:04:28 AM10/15/18
to
Yes, the technique has its limitations, although I would say that
someone who doesn't realize that two operations under separate locking
of the mutex are not mutually atomic probably should not be programming
with threads.

In that kind of case I suppose you are either stuck with default
constructing x and y and then assigning to them in the constructor body
under the same lock, or putting x and y in a private struct and
initializing the struct. Perhaps others can think of other ways.
0 new messages