
on Wed Apr 04 2012, Sumant Tambe <sutambe-AT-gmail.com> wrote:
On 3 April 2012 10:44, Dave Abrahams <dave@boostpro.com> 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