Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Using explicit constructors for C++0x "return { expr };"

Reply
Thread Tools

Using explicit constructors for C++0x "return { expr };"

 
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-27-2010
I'm wondering about this one:

struct ALongAndComplexTypeName {
explicit A(int);
};

/* Possibly intermixed with enable_if huh */
ALongAndComplexTypeName f() {
return {5};
}

I would like to do something like that to avoid having to repeat the return
type name. Since return value copying forbids using explicit copy
constructors, the above will fail. Is there any work around?
 
Reply With Quote
 
 
 
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-27-2010
* Johannes Schaub (litb), on 27.09.2010 04:47:
> I'm wondering about this one:
>
> struct ALongAndComplexTypeName {
> explicit A(int);
> };
>
> /* Possibly intermixed with enable_if huh */
> ALongAndComplexTypeName f() {
> return {5};
> }
>
> I would like to do something like that to avoid having to repeat the return
> type name. Since return value copying forbids using explicit copy
> constructors, the above will fail. Is there any work around?


<code>
namespace detail {
struct A // ALongAndComplexTypename
{
//A( A const& );
//A& operator=( A const& );

explicit A( int ) {}
};

A f() { return A( 5 ); }
}

typedef detail::A ALongAndComplexTypename;
using detail::f;

int main()
{
f();
}
</code>

But if you're not willing to put up with ALongAndComplexTypename in your own
implementation code, why are you foisting that name on client code?

Apart from that,


Cheers, & enjoy!,

- Alf

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
 
 
 
Luc Danton
Guest
Posts: n/a
 
      09-27-2010
On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
> * Johannes Schaub (litb), on 27.09.2010 04:47:
>> I'm wondering about this one:
>>
>> struct ALongAndComplexTypeName {
>> explicit A(int);
>> };
>>
>> /* Possibly intermixed with enable_if huh */
>> ALongAndComplexTypeName f() {
>> return {5};
>> }
>>
>> I would like to do something like that to avoid having to repeat the
>> return
>> type name. Since return value copying forbids using explicit copy
>> constructors, the above will fail. Is there any work around?

>
> <code>
> namespace detail {
> struct A // ALongAndComplexTypename
> {
> //A( A const& );
> //A& operator=( A const& );
>
> explicit A( int ) {}
> };
>
> A f() { return A( 5 ); }
> }
>
> typedef detail::A ALongAndComplexTypename;
> using detail::f;
>
> int main()
> {
> f();
> }
> </code>
>
> But if you're not willing to put up with ALongAndComplexTypename in your
> own implementation code, why are you foisting that name on client code?
>
> Apart from that,
>
>
> Cheers, & enjoy!,
>
> - Alf
>


I suspect what was meant by a long typename is something like

typename std::conditional<
some_check<some_template_param>::value,
something,
something_else
>::type


where something & something else could both be constructed from the same
expression but are not convertible from one to the other.

In that (possibly contrived) case, you'd need to repeat yourself in the
body:

{
/* whatever */
typedef typename std::conditional<
some_check<some_template_param>::value,
something,
something_else
>::type ret_t;
return ret_t { some_expression };
}
 
Reply With Quote
 
Alf P. Steinbach /Usenet
Guest
Posts: n/a
 
      09-27-2010
* Luc Danton, on 27.09.2010 07:28:
> On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
>> * Johannes Schaub (litb), on 27.09.2010 04:47:
>>> I'm wondering about this one:
>>>
>>> struct ALongAndComplexTypeName {
>>> explicit A(int);
>>> };
>>>
>>> /* Possibly intermixed with enable_if huh */
>>> ALongAndComplexTypeName f() {
>>> return {5};
>>> }
>>>
>>> I would like to do something like that to avoid having to repeat the
>>> return
>>> type name. Since return value copying forbids using explicit copy
>>> constructors, the above will fail. Is there any work around?

>>
>> <code>
>> namespace detail {
>> struct A // ALongAndComplexTypename
>> {
>> //A( A const& );
>> //A& operator=( A const& );
>>
>> explicit A( int ) {}
>> };
>>
>> A f() { return A( 5 ); }
>> }
>>
>> typedef detail::A ALongAndComplexTypename;
>> using detail::f;
>>
>> int main()
>> {
>> f();
>> }
>> </code>
>>
>> But if you're not willing to put up with ALongAndComplexTypename in your
>> own implementation code, why are you foisting that name on client code?
>>
>> Apart from that,
>>
>>
>> Cheers, & enjoy!,
>>
>> - Alf
>>

>
> I suspect what was meant by a long typename is something like
>
> typename std::conditional<
> some_check<some_template_param>::value,
> something,
> something_else
> >::type

>
> where something & something else could both be constructed from the same
> expression but are not convertible from one to the other.
>
> In that (possibly contrived) case, you'd need to repeat yourself in the body:
>
> {
> /* whatever */
> typedef typename std::conditional<
> some_check<some_template_param>::value,
> something,
> something_else
> >::type ret_t;

> return ret_t { some_expression };
> }


Uh, I was going to ask why you all are using needless C++0x syntax, but then I
discovered the thread's subject line.

Just a hint: when using MoronBird 3.x[1] you can avoid some of the quoting bugs
by selecting all the text before hitting "reply".

Anyways, I recall vaguely that in C++0x one can write something after the
function head? And perhaps there's a result_of or something in the standard lib?
Too busy to check right now, but worth checking I think!


Cheers,

- Alf

Notes:
[1] In versions 1.x known as ThunderBird.

--
blog at <url: http://alfps.wordpress.com>
 
Reply With Quote
 
Luc Danton
Guest
Posts: n/a
 
      09-27-2010
On 27/09/2010 08:38, Alf P. Steinbach /Usenet wrote:
> * Luc Danton, on 27.09.2010 07:28:
>> On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
>>> * Johannes Schaub (litb), on 27.09.2010 04:47:
>>>> I'm wondering about this one:
>>>>
>>>> struct ALongAndComplexTypeName {
>>>> explicit A(int);
>>>> };
>>>>
>>>> /* Possibly intermixed with enable_if huh */
>>>> ALongAndComplexTypeName f() {
>>>> return {5};
>>>> }
>>>>
>>>> I would like to do something like that to avoid having to repeat the
>>>> return
>>>> type name. Since return value copying forbids using explicit copy
>>>> constructors, the above will fail. Is there any work around?
>>>
>>> <code>
>>> namespace detail {
>>> struct A // ALongAndComplexTypename
>>> {
>>> //A( A const& );
>>> //A& operator=( A const& );
>>>
>>> explicit A( int ) {}
>>> };
>>>
>>> A f() { return A( 5 ); }
>>> }
>>>
>>> typedef detail::A ALongAndComplexTypename;
>>> using detail::f;
>>>
>>> int main()
>>> {
>>> f();
>>> }
>>> </code>
>>>
>>> But if you're not willing to put up with ALongAndComplexTypename in your
>>> own implementation code, why are you foisting that name on client code?
>>>
>>> Apart from that,
>>>
>>>
>>> Cheers, & enjoy!,
>>>
>>> - Alf
>>>

>>
>> I suspect what was meant by a long typename is something like
>>
>> typename std::conditional<
>> some_check<some_template_param>::value,
>> something,
>> something_else
>> >::type

>>
>> where something & something else could both be constructed from the same
>> expression but are not convertible from one to the other.
>>
>> In that (possibly contrived) case, you'd need to repeat yourself in
>> the body:
>>
>> {
>> /* whatever */
>> typedef typename std::conditional<
>> some_check<some_template_param>::value,
>> something,
>> something_else
>> >::type ret_t;

>> return ret_t { some_expression };
>> }

>
> Uh, I was going to ask why you all are using needless C++0x syntax, but
> then I discovered the thread's subject line.
>
> Just a hint: when using MoronBird 3.x[1] you can avoid some of the
> quoting bugs by selecting all the text before hitting "reply".
>
> Anyways, I recall vaguely that in C++0x one can write something after
> the function head? And perhaps there's a result_of or something in the
> standard lib? Too busy to check right now, but worth checking I think!


There is the delayed return type syntax yes.

int func();
can be written
auto func() -> int;

But its purpose is to have the parameters in scope (or declared? Not
sure about the proper terminology):

template<typename T>
auto func(T t)
-> decltype(*t);

You still can't factor out a type via a typedef (or using decl.) before
getting in the function body proper, where it's too late. std::result_of
is of no help either (I think).

However I've seen Boost.MPL use a "result_of" namespace, which doubles
up not only as a helper for convenient typedefs, but is actually used
for meta-computations. I think that's the proper way to "hide" those
computations from the user (if that's needed) and make maintenance
easier (but YMMV), and I use it too. So my own example would look like:

namespace result_of {

template</* whatever */>
struct f {
typedef typename std::conditional<
some_value,
something,
something
>::type type;
};

} // result_of

// Back in enclosing namespace
template</* whatever */>
typename result_of::f< ... >::type
f( ... )
{
...
typedef typename result_of::f< ... >::type ret_t;
return ret_t { /* something */ };
}


Also thanks for the TB tip; although I never noticed anything wrong with
the quotes.
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-27-2010
Luc Danton wrote:

> On 27/09/2010 08:38, Alf P. Steinbach /Usenet wrote:
>> * Luc Danton, on 27.09.2010 07:28:
>>> On 27/09/2010 06:39, Alf P. Steinbach /Usenet wrote:
>>>> * Johannes Schaub (litb), on 27.09.2010 04:47:
>>>>> I'm wondering about this one:
>>>>>
>>>>> struct ALongAndComplexTypeName {
>>>>> explicit A(int);
>>>>> };
>>>>>
>>>>> /* Possibly intermixed with enable_if huh */
>>>>> ALongAndComplexTypeName f() {
>>>>> return {5};
>>>>> }
>>>>>
>>>>> I would like to do something like that to avoid having to repeat the
>>>>> return
>>>>> type name. Since return value copying forbids using explicit copy
>>>>> constructors, the above will fail. Is there any work around?
>>>>
>>>> <code>
>>>> namespace detail {
>>>> struct A // ALongAndComplexTypename
>>>> {
>>>> //A( A const& );
>>>> //A& operator=( A const& );
>>>>
>>>> explicit A( int ) {}
>>>> };
>>>>
>>>> A f() { return A( 5 ); }
>>>> }
>>>>
>>>> typedef detail::A ALongAndComplexTypename;
>>>> using detail::f;
>>>>
>>>> int main()
>>>> {
>>>> f();
>>>> }
>>>> </code>
>>>>
>>>> But if you're not willing to put up with ALongAndComplexTypename in
>>>> your own implementation code, why are you foisting that name on client
>>>> code?
>>>>
>>>> Apart from that,
>>>>
>>>>
>>>> Cheers, & enjoy!,
>>>>
>>>> - Alf
>>>>
>>>
>>> I suspect what was meant by a long typename is something like
>>>
>>> typename std::conditional<
>>> some_check<some_template_param>::value,
>>> something,
>>> something_else
>>> >::type
>>>
>>> where something & something else could both be constructed from the same
>>> expression but are not convertible from one to the other.
>>>
>>> In that (possibly contrived) case, you'd need to repeat yourself in
>>> the body:
>>>
>>> {
>>> /* whatever */
>>> typedef typename std::conditional<
>>> some_check<some_template_param>::value,
>>> something,
>>> something_else
>>> >::type ret_t;
>>> return ret_t { some_expression };
>>> }

>>
>> Uh, I was going to ask why you all are using needless C++0x syntax, but
>> then I discovered the thread's subject line.
>>
>> Just a hint: when using MoronBird 3.x[1] you can avoid some of the
>> quoting bugs by selecting all the text before hitting "reply".
>>
>> Anyways, I recall vaguely that in C++0x one can write something after
>> the function head? And perhaps there's a result_of or something in the
>> standard lib? Too busy to check right now, but worth checking I think!
>>

>
> There is the delayed return type syntax yes.
>
> int func();
> can be written
> auto func() -> int;
>
> But its purpose is to have the parameters in scope (or declared? Not
> sure about the proper terminology):
>
> template<typename T>
> auto func(T t)
> -> decltype(*t);
>
> You still can't factor out a type via a typedef (or using decl.) before
> getting in the function body proper, where it's too late. std::result_of
> is of no help either (I think).
>


One very ugly thing to do is to use default template arguments

template<typename T, typename R = decltype(*declval<T>())>
R func(T t) {
return { *t };
}

 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      09-27-2010
On 27 Sep., 04:47, Johannes Schaub wrote:
> I'm wondering about this one:
>
> struct ALongAndComplexTypeName {
> * explicit A(int);
> };
>
> /* Possibly intermixed with enable_if huh */
> ALongAndComplexTypeName f() {
> * return {5};
> }
>
> I would like to do something like that to avoid having to repeat the return
> type name. Since return value copying forbids using explicit copy
> constructors, the above will fail.


ALongAndComplexTypeName *has* an implicit copy constructor but only an
explicit constructor taking an int. I think you're right, the code
won't work. The upcoming standard refers to this kind of
initialization as a "copy-list-initialization" which I guess belongs
to the copy-initialization category. So the explicit A(int) is not
viable for this kind of initialization.

After thinking about it, it seems that with C++0x's list
initialization for non-aggregate classes the keyword 'export' has also
an effect on constructors that take more than one parameter (ignoring
defaut arguments) -- unlike before in C++03. I havn't realized this
until now. So, if you don't want class type T

class T {
public:
T(int, double, void*);
};

to be copy-list-initializable we can make this constructor explicit so
that

void foo() {
T x = {1729,3.14159,nullptr};
}

won't compile anymore. Interesting.

> Is there any work around?


I don't see any. This doesn't really strike me as a big annoyance,
though.

Cheers!
SG
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-27-2010
SG wrote:

> On 27 Sep., 04:47, Johannes Schaub wrote:
>> I'm wondering about this one:
>>
>> struct ALongAndComplexTypeName {
>> explicit A(int);
>> };
>>
>> /* Possibly intermixed with enable_if huh */
>> ALongAndComplexTypeName f() {
>> return {5};
>> }
>>
>> I would like to do something like that to avoid having to repeat the
>> return type name. Since return value copying forbids using explicit copy
>> constructors, the above will fail.

>
> ALongAndComplexTypeName *has* an implicit copy constructor but only an
> explicit constructor taking an int. I think you're right, the code
> won't work. The upcoming standard refers to this kind of
> initialization as a "copy-list-initialization" which I guess belongs
> to the copy-initialization category. So the explicit A(int) is not
> viable for this kind of initialization.
>


The worst, in my opinion, is that the constructor *is* viable, but it is not
allowed to be used.

struct A {
explicit A(bool);
A(std::string);
};

// error! explicit constructor used
A a = { "hello folks" };

// fine! A(bool) not viable
A a = "hello folks";

I dunno why that is desirable but it appears to me that it will have bad
effects on overload resolution.

struct Vector {
explicit Vector(int len);
};

struct MyInt {
MyInt(int I);
};

void print(Vector v);
void print(MyInt i);

// fine, call MyInt version
print(42);

// oops, ambiguity
print({42});

> After thinking about it, it seems that with C++0x's list
> initialization for non-aggregate classes the keyword 'export' has also
> an effect on constructors that take more than one parameter (ignoring
> defaut arguments) -- unlike before in C++03. I havn't realized this
> until now. So, if you don't want class type T
>
> class T {
> public:
> T(int, double, void*);
> };
>
> to be copy-list-initializable we can make this constructor explicit so
> that
>
> void foo() {
> T x = {1729,3.14159,nullptr};
> }
>
> won't compile anymore. Interesting.
>


I agree that this is intriguing, I have not thought about that case before.
There was until recently a way to do the same for a zero-parameter default
constructor

struct T {
explicit T();
};

// ill-formed! constructor is explicit
T t = { };

This was changed in the FCD though, because now it disregards overload
resolution and just calls that constructor as part of value initialization.
It wasn't all that useful anyway, I think




 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      09-27-2010
On 27 Sep., 16:09, "Johannes Schaub (litb)" wrote:
>
> struct A {
> * explicit A(bool);
> * A(std::string);
> };
>
> // error! explicit constructor used
> A a = { "hello folks" };


To my surprize I got the following error using G++ 4.4.1:

otest.cpp: In function 'int main()':
otest.cpp:13: error: call of overloaded 'A(<brace-enclosed
initializer list>)' is ambiguous
otest.cpp:8: note: candidates are: A::A(std::string)
otest.cpp:6: note: A::A(const A&)

I actually expected it to work and use A::A(string).
Hmm...

> // fine! A(bool) not viable
> A a = "hello folks";


This doesn't compile either using G++ 4.4.1:

otest.cpp:13: error: conversion from 'const char [12]' to
non-scalar type 'A' requested

I also expected this to work and use A::A(string).

> I dunno why that is desirable but it appears to me that it will have bad
> effects on overload resolution.
>
> struct Vector {
> * explicit Vector(int len);
> };
>
> struct MyInt {
> * MyInt(int I);
> };
>
> void print(Vector v);
> void print(MyInt i);
>
> // fine, call MyInt version
> print(42);


Ok, makes sense.

> // oops, ambiguity
> print({42});


Why is this supposed to be an ambiguity?

> SG wrote:
> > After thinking about it, it seems that with C++0x's list
> > initialization for non-aggregate classes the keyword 'export' has also
> > an effect on constructors that take more than one parameter (ignoring
> > defaut arguments) -- unlike before in C++03. I havn't realized this
> > until now. So, if you don't want class type T


Of course, I meant to write "explicit", not "export".

> > * class T {
> > * public:
> > * * T(int, double, void*);
> > * };

>
> > to be copy-list-initializable we can make this constructor explicit so
> > that

>
> > * void foo() {
> > * * T x = {1729,3.14159,nullptr};
> > * }

>
> > won't compile anymore. Interesting.

>
> I agree that this is intriguing, I have not thought about that case before.
> There was until recently a way to do the same for a zero-parameter default
> constructor
>
> struct T {
> *explicit T();
> };
>
> // ill-formed! constructor is explicit
> T t = { };


Why would you even write this? T t{}; should do the trick.

> This was changed in the FCD though, because now it disregards overload
> resolution and just calls that constructor as part of value initialization.
> It wasn't all that useful anyway, I think


I currently don't claim to understand initialization. It sounds like
an awful lot of special cases to me. And *I* thought, it's going to
get more intuitive...

Cheers!
SG
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      09-27-2010
Johannes Schaub (litb) wrote:

> SG wrote:
>
>> On 27 Sep., 16:09, "Johannes Schaub (litb)" wrote:
>>>
>>> struct A {
>>> explicit A(bool);
>>> A(std::string);
>>> };
>>>
>>> // error! explicit constructor used
>>> A a = { "hello folks" };

>>
>> To my surprize I got the following error using G++ 4.4.1:
>>
>> otest.cpp: In function 'int main()':
>> otest.cpp:13: error: call of overloaded 'A(<brace-enclosed
>> initializer list>)' is ambiguous
>> otest.cpp:8: note: candidates are: A::A(std::string)
>> otest.cpp:6: note: A::A(const A&)
>>
>> I actually expected it to work and use A::A(string).
>> Hmm...
>>

>
> Yes, I think this should use A::A(string). Copy-list-initialization has no
> restriction onto nested user defined conversions as opposed to plain copy
> initialization. I think the error here is that GCC4.4 does disregard
> 13.3.3.1/4 which says "by 13.3.1.7 (when passing the initializer list as a
> single argument or when the initializer list has exactly one element) and
> (a conversion to some class X or reference to (possibly cv-qualified) X is
> considered for the first parameter of a constructor of X)" (parens by me to
> hilight the binding of "and" and "or" in the wording, which I find easily
> confused because "and" usually binds tighter
>
> This is intended to factor out the copy constructor for list
> initialization because since we are allowed to use nested user defined
> conversions, we could always produce an ambiguous second conversion path
> by first invoking the copy constructor and then doing the same as we did
> for the other conversions.
>


Sorry I meant that it should error out because it uses the explicit
constructor (standard conversion sequence is better than user defined
conversion sequence). But that GCC4.4's ambiguity error message is out of
order: It should not consider the copy constructor.


 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Syntax bug, in 1.8.5? return not (some expr) <-- syntax error vsreturn (not (some expr)) <-- fine Good Night Moon Ruby 9 07-25-2007 04:51 PM
Explicit instantiation of STL vector demands explicit instantiation of all the templates it using internally. krunalbauskar@gmail.com C++ 1 12-25-2006 03:51 PM
What's the difference betwwen explicit instantiaion and explicit specialization? Andy C++ 5 01-30-2005 11:46 PM
explicit copy constructors Dave C++ 2 01-15-2005 04:32 AM
Is explicit template qualification required for explicit delete? J.T. Conklin C++ 1 08-11-2004 02:06 AM



Advertisments