Skip to content

Instantly share code, notes, and snippets.

@splinterofchaos
Created November 10, 2012 04:47
Show Gist options
  • Save splinterofchaos/4049946 to your computer and use it in GitHub Desktop.
Save splinterofchaos/4049946 to your computer and use it in GitHub Desktop.
Construct Chainable -- A demonstration of a left-associative type constructor.
#include <utility>
#include <iostream>
// http://en.cppreference.com/w/cpp/types/decay
template< class X >
using Decay = typename std::decay<X>::type;
// ConstructBinary<T>(X,Y) = T<X,Y>
template< template<class...> class X >
struct ConstructBinary {
// Construct(const X&,const Y&) = T<X,Y>
template< class Y, class Z, class R = X< Decay<Y>, Decay<Z> > >
constexpr R operator () ( Y&& y, Z&& z ) {
return R( std::forward<Y>(y), std::forward<Z>(z) );
}
};
// Addition is an example of an associative operation.
// x + y + z = (z+y) + z
// If we let (+) be an operation that creates a pair, then
// x (+) y = (x,y)
// And if we consider this operation left associative (meaning from left to right),
// x (+) y (+) z = (z,y) (+) z = ((z,y),z)
// If we think of this as working on some type, T,
// X (+) Y (+) Z = T(X,Y) (+) Z = T( T(X,Y), Z )
template< template<class...> class X >
struct ConstructChainable : ConstructBinary<X> {
using Self = ConstructChainable<X>;
using ConstructBinary<X>::operator();
template< class Y, class Z, class A, class ...B,
class R1 = X< Decay<Y>, Decay<Z> >,
class R2 = decltype( Self()( std::declval<R1>(), std::declval<A>(),
std::declval<B>()... ) ) >
constexpr R2 operator () ( Y&& y, Z&& z, A&& a, B&& ...b ) {
return (*this) (
R1( std::forward<Y>(y), std::forward<Z>(z) ),
std::forward<A>(a), std::forward<B>(b)...
);
}
};
constexpr auto make_pair = ConstructBinary< std::pair >();
constexpr auto make_tuple = ConstructChainable< std::pair >();
template< class X, class Y >
std::ostream& operator << ( std::ostream& os, const std::pair<X,Y>& p ) {
os << '(' << p.first << ',' << p.second << ')';
return os;
}
int main() {
std::cout << make_pair(1,2) << std::endl; // p(1,2)
std::cout << make_tuple(1,2,3,4,5) << std::endl; // p( p( p( p(1,2), 3 ), 4 ) 5 )
}
@splinterofchaos
Copy link
Author

The output is (((((1,2),3),4),5)

@keveman
Copy link

keveman commented Nov 10, 2012

What's all the Decay stuff for?

@splinterofchaos
Copy link
Author

The idea is to perfect forward the arguments, but the container is supposed to contain the values. So, if passed in a int&, I want the pair to hold an int. Of coarse, you could also write a version that forwarded the type, too.

@splinterofchaos
Copy link
Author

Oh, and if you're not familiar with std::decay, it's basically like remove_reference, remove_const, and a couple other things, all rolled in to one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment