Created
November 10, 2012 04:47
-
-
Save splinterofchaos/4049946 to your computer and use it in GitHub Desktop.
Construct Chainable -- A demonstration of a left-associative type constructor.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 ) | |
} |
What's all the Decay stuff for?
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.
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
The output is (((((1,2),3),4),5)