Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > Perfect Forwarding + static_assert [C++0x]

Reply
Thread Tools

Perfect Forwarding + static_assert [C++0x]

 
 
Scott Meyers
Guest
Posts: n/a
 
      12-04-2010
Suppose I have a setter function, and I'd like it to forward its argument as an
lvalue or an rvalue to whatever constructor will be used to do the setting. I
can overload the setter like this:

class Widget {
public:
...
void setName(const std::string& newName) // set from lvalue
{ name = newName; }

void setName(std::string&& newName) // set from rvalue
{ name = std::move(newName); }

...
private:
std::string name;
};

I can also use a template member function and perfect forwarding:

class Widget {
public:
...
template<typename T>
void setName(T&& newName)
{ name = std::forward<T>(newName); }
...
};

The template will forward any type that can be used to initialize the string,
i.e., it will accept types other than std::string. Suppose, for whatever wacky
reason, I really want to forward only a std::string. I came up with this:

template<typename T>
void setName(T&& newName)
{
static_assert(std::is_same<std::remove_cv<std::rem ove_reference<T>::type
>::type,

std::string
>::value,

"T must be a [const] std::string“
);

name = std::forward<T>(newName);
};

VC10 swallows it and seems to behave the way I want. gcc 4.5 doesn't compile
it. Questions:

1. Is there some reason the above should not compile?
2. Assuming I want to do what I say I want to do, is there a better way to do
it? I assume I could also play games with enable_if, but I think the
incantation would be no simpler than the static_assert.

Thanks,

Scott

--
* C++ and Beyond Encore!: Meyers, Sutter, & Alexandrescu, Dec. 13-16 near
Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or
personal use (http://tinyurl.com/yl5ka5p).
 
Reply With Quote
 
 
 
 
Marc
Guest
Posts: n/a
 
      12-04-2010
Scott Meyers wrote:

> template<typename T>
> void setName(T&& newName)
> {
> static_assert(std::is_same<std::remove_cv<std::rem ove_reference<T>::type
> >::type,

> std::string
> >::value,

> "T must be a [const] std::string“
> );
>
> name = std::forward<T>(newName);
> };
>
> VC10 swallows it and seems to behave the way I want. gcc 4.5 doesn't compile
> it. Questions:
>
> 1. Is there some reason the above should not compile?


Missing "typename"?

> 2. Assuming I want to do what I say I want to do, is there a better way to do
> it? I assume I could also play games with enable_if, but I think the
> incantation would be no simpler than the static_assert.


The incantation would be roughly the same, but SFINAE would apply.
 
Reply With Quote
 
 
 
 
SG
Guest
Posts: n/a
 
      12-04-2010
On 4 Dez., 07:07, Scott Meyers wrote:
> [...]
> I really want to forward only a std::string. I came up with this:
>
> * template<typename T>
> * void setName(T&& newName)
> * {
> static_assert(
> std::is_same<
> std::remove_cv<
> std::remove_reference<T>::type
> * * * * * *>::type,
> * * * * * *std::string
> * * * * >::value, "T must be a [const] std::string
> * * *);
>
> * * *name = std::forward<T>(newName);
> * *};


As Marc said already, a "typename" appears to be missing. In addition,
I'll like to mention that std::decay typically works as a shortcut for
remove_cv<remove_refernence<...>>

> VC10 swallows it and seems to behave the way I want.
> gcc 4.5 doesn't compile


I guess that's because VC10 doesn't do a proper two-phase lookup.

> 2. Assuming I want to do what I say I want to do, is there a
> better way to do it? *I assume I could also play games with
> enable_if, but I think the incantation would be no simpler
> than the static_assert.


I was just about to suggest enable_if here. That's seems (at least for
function templates) like a good way to constrain them in order to
reduce the size of the overload resolution set. With a failing
enable_if a function doesn't make it into the overload resolution set
while a static_assert would only be checked after overload resolution.
Instead of restricting the parameter to std::string (or references to
string), you should consider conversion, so that you can also pass
string literals:

template<class T>
enable_if< is_convertible<T,string>::value,
void>::type setName(T&& newName)
{
name_ = forward<T>(newName);
}

To hide the template stuff one could use a wrapper that remembers the
address of the argument object and its value category so it can later
perform the corresponding assignment:

template<class T>
class epa // ep = efficient passing / assignment
{
public:
epa(T const& x) : p(&x), q(0) {}
epa(T && x) : p(0), q(&x) {}
void assign_to(T & target) {
if (p) target = *p;
else target = move(*q);
}
private:
T const* p;
T * q;
};

void YourClass:setName(epa<string> newName)
{
newName.assign_to(this->name);
}


Cheers!
Sebastian
 
Reply With Quote
 
SG
Guest
Posts: n/a
 
      12-04-2010
On 4 Dez., 13:56, SG wrote:
> [...]
>
> * void YourClass:setName(epa<string> newName)
> * {
> * * * newName.assign_to(this->name);
> * }


But apssing string literals won't work anymore because two user-
defined conversions would be involved.

Cheers!
SG
 
Reply With Quote
 
Scott Meyers
Guest
Posts: n/a
 
      12-04-2010
On 12/4/2010 4:56 AM, SG wrote:
> As Marc said already, a "typename" appears to be missing. In addition,
> I'll like to mention that std::decay typically works as a shortcut for
> remove_cv<remove_refernence<...>>


Nice catch to you both on the "typename" issue (duh), and thanks for the pointer
to std::decay, which I did not know about.

> Instead of restricting the parameter to std::string (or references to
> string), you should consider conversion, so that you can also pass
> string literals:
>
> template<class T>
> enable_if< is_convertible<T,string>::value,
> void>::type setName(T&& newName)
> {
> name_ = forward<T>(newName);
> }


But if I wanted to allow conversions, I could just skip the static_assert (or
enable_if), because the original code will take anything and forward it to a
std::string constructor. That code will compile only if the type passed is
convertible to a std::string.

Scott

--
* C++ and Beyond Encore!: Meyers, Sutter, & Alexandrescu, Dec. 13-16 near
Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or
personal use (http://tinyurl.com/yl5ka5p).
 
Reply With Quote
 
Johannes Schaub (litb)
Guest
Posts: n/a
 
      12-05-2010
Scott Meyers wrote:

> Suppose I have a setter function, and I'd like it to forward its argument
> as an
> lvalue or an rvalue to whatever constructor will be used to do the
> setting. I can overload the setter like this:
>
> class Widget {
> public:
> ...
> void setName(const std::string& newName) // set from lvalue
> { name = newName; }
>
> void setName(std::string&& newName) // set from rvalue
> { name = std::move(newName); }
>
> ...
> private:
> std::string name;
> };
>
> I can also use a template member function and perfect forwarding:
>
> class Widget {
> public:
> ...
> template<typename T>
> void setName(T&& newName)
> { name = std::forward<T>(newName); }
> ...
> };
>


Notice that the first code is superior IMO because it allows the caller to
choose between list initialization and non-list initialization.

> The template will forward any type that can be used to initialize the
> string,
> i.e., it will accept types other than std::string. Suppose, for whatever
> wacky
> reason, I really want to forward only a std::string. I came up with this:
>
> template<typename T>
> void setName(T&& newName)
> {
>

static_assert(std::is_same<std::remove_cv<std::rem ove_reference<T>::type
> >::type,

> std::string
> >::value,

> "T must be a [const] std::string“
> );
>
> name = std::forward<T>(newName);
> };
>
> VC10 swallows it and seems to behave the way I want. gcc 4.5 doesn't
> compile
> it. Questions:
>
> 1. Is there some reason the above should not compile?


I wrote a FAQ about when to place typename and template:
http://stackoverflow.com/questions/6...-template-and-
typename-on-dependent-names/613132#613132 . I would be glad to hear about
your feedback!
 
Reply With Quote
 
Scott Meyers
Guest
Posts: n/a
 
      12-05-2010
On 12/4/2010 9:12 PM, Johannes Schaub (litb) wrote:
> Notice that the first code is superior IMO because it allows the caller to
> choose between list initialization and non-list initialization.


Ah, an infamous case of imperfect forwarding, thanks for reminding me. 0 as a
null pointer can't be perfect-forwarded, either. There are a few other places
where perfect forwarding demonstrates its imperfections. Details are in the
discussion thread at http://tinyurl.com/26wz3f7 .

Scott

--
* C++ and Beyond Encore!: Meyers, Sutter, & Alexandrescu, Dec. 13-16 near
Seattle (http://cppandbeyond.com/)
* License my training materials for commercial (http://tinyurl.com/yfzvkp9) or
personal use (http://tinyurl.com/yl5ka5p).
 
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
Perfect Forwarding in Runtime (rvalue reference) dervih C++ 3 07-13-2012 02:03 PM
using std::function, std::bind, perfect forwarding Andrew Tomazos C++ 1 12-23-2011 10:19 AM
Specializing Perfect Forwarding Templates? Scott Meyers C++ 20 03-16-2011 07:07 PM
Perfect function forwarding Alexis Nikichine Javascript 6 12-28-2005 02:39 PM
Sharing a trivial static_assert meta-function, any comments? christopher diggins C++ 0 05-16-2005 08:51 PM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57