|
Boost : |
Subject: Re: [boost] C++11 Metaprogramming
From: Dave Abrahams (dave_at_[hidden])
Date: 2012-04-05 01:17:57
on Wed Apr 04 2012, Sumant Tambe <sutambe-AT-gmail.com> wrote:
> On 3 April 2012 10:44, Dave Abrahams <dave_at_[hidden]> wrote:
>
>>
>> But isn't this a better way to relieve the tedium?
>>
>> #define RETURNS(...) \
>> noexcept(noexcept(decltype(__VA_ARGS__)(std::move(__VA_ARGS__)))) \
>> -> decltype(__VA_ARGS__) \
>> { return (__VA_ARGS__); } \
>> typedef int RETURNS_CAT(RETURNS_, __LINE__)
>>
>> #define RETURNS_CAT_0(x, y) x ## y
>> #define RETURNS_CAT(x, y) RETURNS_CAT_0(x,y)
>>
>> ...
>>
>> auto swap(B& x, B& y) RETURNS(swap(x.a,y.a), swap(x.b,y.b), ...);
>>
>>
> But is that really allowed? I could not get it to work on clang because
> the declaration of swap needs access to the members of the class and the
> class definition is incomplete at that point.
Going back to Howard's original example:
--8<---------------cut here---------------start------------->8---
struct B
{
A a;
};
void swap(B& x, B& y) noexcept(__is_nothrow_swappable<A>::value)
{
swap(x.a, y.a);
}
--8<---------------cut here---------------end--------------->8---
B is certainly complete at the point swap is defined. I did have to
make an adjustment to deal with the fact that swap returns void:
--8<---------------cut here---------------start------------->8---
#include <utility>
// Used to create a non-void object that we can pass to std::move
// in case the expression is void.
struct nonvoid
{
template <class T>
friend auto operator,(T&& x, nonvoid) noexcept -> T&&;
};
#define RETURNS(...) \
noexcept(noexcept( \
decltype(__VA_ARGS__)( \
std::move(((__VA_ARGS__),nonvoid())) \
))) \
-> decltype(__VA_ARGS__) \
{ return (__VA_ARGS__); } \
typedef int RETURNS_CAT(RETURNS_, __LINE__)
#define RETURNS_CAT_0(x, y) x ## y
#define RETURNS_CAT(x, y) RETURNS_CAT_0(x,y)
struct A
{
friend void swap(A&,A&) noexcept {}
};
struct B
{
A a;
};
auto swap(B& x, B& y)
RETURNS(swap(x.a,y.a));
int main() {}
--8<---------------cut here---------------end--------------->8---
> Moving the definition of swap outside the class did not help because
> you need access to private members. Making swap friend also does not
> help because friend declaration also needs a matching noexcept
> specification. gcc 4.7 seems to be lenient regarding this check but
> clang is not.
I see: with private members, this swap needs to be a friend and then you
have the incomplete type problem:
--8<---------------cut here---------------start------------->8---
class B
{
A a;
friend auto swap(B& x, B& y)
RETURNS(swap(x.a,y.a)); // Error; B is incomplete
};
--8<---------------cut here---------------end--------------->8---
Well, you can get around it by templatizing :-)
--8<---------------cut here---------------start------------->8---
struct A
{
friend void swap(A&,A&) noexcept {}
};
namespace gcc_bug_workaround { // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52875
class B
{
A a;
template <class T>
friend auto swap(T& x, T& y)
RETURNS(swap(x.a,y.a)); // Error; B is incomplete
};
}
using namespace gcc_bug_workaround;
--8<---------------cut here---------------end--------------->8---
> Alternatively, using std::tuple could help in reducing verbosity of
> noexcept specification Howard is alluding to.
>
<snip>
> However, I don't think if we could use std::make_tuple to avoid spelling
> out types all over again due to the reasons mentioned above.
>
> I would love to stand corrected.
I think the template trick serves to delay things till the class is
defined, yes?
-- Dave Abrahams BoostPro Computing http://www.boostpro.com
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk