diff --git a/include/Rivet/Math/MathUtils.hh b/include/Rivet/Math/MathUtils.hh
--- a/include/Rivet/Math/MathUtils.hh
+++ b/include/Rivet/Math/MathUtils.hh
@@ -1,597 +1,588 @@
 // -*- C++ -*-
 #ifndef RIVET_MathUtils_HH
 #define RIVET_MathUtils_HH
 
 #include "Rivet/Math/MathHeader.hh"
 #include <type_traits>
 #include <cassert>
 
 namespace Rivet {
 
 
   /// @name Comparison functions for safe (floating point) equality tests
   //@{
 
   /// @brief Compare a number to zero
   ///
   /// This version for floating point types has a degree of fuzziness expressed
   /// by the absolute @a tolerance parameter, for floating point safety.
   template <typename NUM>
   inline typename std::enable_if<std::is_floating_point<NUM>::value, bool>::type
   isZero(NUM val, double tolerance=1e-8) {
     return fabs(val) < tolerance;
   }
 
   /// @brief Compare a number to zero
   ///
   /// SFINAE template specialisation for integers, since there is no FP
   /// precision issue.
   template <typename NUM>
   inline typename std::enable_if<std::is_integral<NUM>::value, bool>::type
   isZero(NUM val, double=1e-5) { //< NB. unused tolerance parameter for ints, still needs a default value!
     return val == 0;
   }
 
 
   /// @brief Compare two numbers for equality with a degree of fuzziness
   ///
   /// This version for floating point types (if any argument is FP) has a degree
   /// of fuzziness expressed by the fractional @a tolerance parameter, for
   /// floating point safety.
   template <typename N1, typename N2>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value &&
    (std::is_floating_point<N1>::value || std::is_floating_point<N2>::value), bool>::type
   fuzzyEquals(N1 a, N2 b, double tolerance=1e-5) {
     const double absavg = (std::abs(a) + std::abs(b))/2.0;
     const double absdiff = std::abs(a - b);
     const bool rtn = (isZero(a) && isZero(b)) || absdiff < tolerance*absavg;
     return rtn;
   }
 
   /// @brief Compare two numbers for equality with a degree of fuzziness
   ///
   /// Simpler SFINAE template specialisation for integers, since there is no FP
   /// precision issue.
   template <typename N1, typename N2>
   inline typename std::enable_if<
     std::is_integral<N1>::value && std::is_integral<N2>::value, bool>::type
     fuzzyEquals(N1 a, N2 b, double) { //< NB. unused tolerance parameter for ints, still needs a default value!
     return a == b;
   }
 
 
   /// @brief Compare two numbers for >= with a degree of fuzziness
   ///
   /// The @a tolerance parameter on the equality test is as for @c fuzzyEquals.
   template <typename N1, typename N2>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value, bool>::type
   fuzzyGtrEquals(N1 a, N2 b, double tolerance=1e-5) {
     return a > b || fuzzyEquals(a, b, tolerance);
   }
 
 
   /// @brief Compare two floating point numbers for <= with a degree of fuzziness
   ///
   /// The @a tolerance parameter on the equality test is as for @c fuzzyEquals.
   template <typename N1, typename N2>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value, bool>::type
   fuzzyLessEquals(N1 a, N2 b, double tolerance=1e-5) {
     return a < b || fuzzyEquals(a, b, tolerance);
   }
 
   //@}
 
 
   /// @name Ranges and intervals
   //@{
 
   /// Represents whether an interval is open (non-inclusive) or closed (inclusive).
   ///
   /// For example, the interval \f$ [0, \pi) \f$ is closed (an inclusive
   /// boundary) at 0, and open (a non-inclusive boundary) at \f$ \pi \f$.
   enum RangeBoundary { OPEN=0, SOFT=0, CLOSED=1, HARD=1 };
 
   /// @brief Determine if @a value is in the range @a low to @a high, for floating point numbers
   ///
   /// Interval boundary types are defined by @a lowbound and @a highbound.
   template <typename N1, typename N2, typename N3>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
   inRange(N1 value, N2 low, N3 high,
           RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
     if (lowbound == OPEN && highbound == OPEN) {
       return (value > low && value < high);
     } else if (lowbound == OPEN && highbound == CLOSED) {
       return (value > low && value <= high);
     } else if (lowbound == CLOSED && highbound == OPEN) {
       return (value >= low && value < high);
     } else { // if (lowbound == CLOSED && highbound == CLOSED) {
       return (value >= low && value <= high);
     }
   }
 
   /// @brief Determine if @a value is in the range @a low to @a high, for floating point numbers
   ///
   /// Interval boundary types are defined by @a lowbound and @a highbound.
   /// Closed intervals are compared fuzzily.
   template <typename N1, typename N2, typename N3>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
   fuzzyInRange(N1 value, N2 low, N3 high,
                RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
     if (lowbound == OPEN && highbound == OPEN) {
       return (value > low && value < high);
     } else if (lowbound == OPEN && highbound == CLOSED) {
       return (value > low && fuzzyLessEquals(value, high));
     } else if (lowbound == CLOSED && highbound == OPEN) {
       return (fuzzyGtrEquals(value, low) && value < high);
     } else { // if (lowbound == CLOSED && highbound == CLOSED) {
       return (fuzzyGtrEquals(value, low) && fuzzyLessEquals(value, high));
     }
   }
 
   /// Alternative version of inRange which accepts a pair for the range arguments.
   template <typename N1, typename N2, typename N3>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
   inRange(N1 value, pair<N2, N3> lowhigh,
           RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
     return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
   }
 
 
   // Alternative forms, with snake_case names and boundary types in names rather than as args -- from MCUtils
 
   /// @brief Boolean function to determine if @a value is within the given range
   ///
   /// @note The interval is closed (inclusive) at the low end, and open (exclusive) at the high end.
   template <typename N1, typename N2, typename N3>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
   in_range(N1 val, N2 low, N3 high) {
     return inRange(val, low, high, CLOSED, OPEN);
   }
 
   /// @brief Boolean function to determine if @a value is within the given range
   ///
   /// @note The interval is closed at both ends.
   template <typename N1, typename N2, typename N3>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
   in_closed_range(N1 val, N2 low, N3 high) {
     return inRange(val, low, high, CLOSED, CLOSED);
   }
 
   /// @brief Boolean function to determine if @a value is within the given range
   ///
   /// @note The interval is open at both ends.
   template <typename N1, typename N2, typename N3>
   inline typename std::enable_if<
     std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
   in_open_range(N1 val, N2 low, N3 high) {
     return inRange(val, low, high, OPEN, OPEN);
   }
 
   /// @todo Add pair-based versions of the named range-boundary functions
 
   //@}
 
 
   /// @name Miscellaneous numerical helpers
   //@{
 
   /// Named number-type squaring operation.
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
   sqr(NUM a) {
     return a*a;
   }
 
   /// @brief Named number-type addition in quadrature operation.
   ///
   /// @note Result has the sqrt operation applied.
   /// @todo When std::common_type can be used, generalise to multiple numeric types with appropriate return type.
   // template <typename N1, typename N2>
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
   //std::common_type<N1, N2>::type
   add_quad(NUM a, NUM b) {
     return sqrt(a*a + b*b);
   }
 
   /// Named number-type addition in quadrature operation.
   ///
   /// @note Result has the sqrt operation applied.
   /// @todo When std::common_type can be used, generalise to multiple numeric types with appropriate return type.
   // template <typename N1, typename N2>
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
   //std::common_type<N1, N2, N3>::type
   add_quad(NUM a, NUM b, NUM c) {
     return sqrt(a*a + b*b + c*c);
   }
 
   /// Return a/b, or @a fail if b = 0
   /// @todo When std::common_type can be used, generalise to multiple numeric types with appropriate return type.
   inline double safediv(double num, double den, double fail=0.0) {
     return (!isZero(den)) ? num/den : fail;
   }
 
   /// A more efficient version of pow for raising numbers to integer powers.
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
   intpow(NUM val, unsigned int exp) {
     assert(exp >= 0);
     if (exp == 0) return (NUM) 1;
     else if (exp == 1) return val;
     return val * intpow(val, exp-1);
   }
 
   /// Find the sign of a number
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, int>::type
   sign(NUM val) {
     if (isZero(val)) return ZERO;
     const int valsign = (val > 0) ? PLUS : MINUS;
     return valsign;
   }
 
   //@}
 
 
   /// @name Physics statistical distributions
   //@{
 
   /// @brief CDF for the Breit-Wigner distribution
   inline double cdfBW(double x, double mu, double gamma) {
     // normalize to (0;1) distribution
     const double xn = (x - mu)/gamma;
     return std::atan(xn)/M_PI + 0.5;
   }
 
   /// @brief Inverse CDF for the Breit-Wigner distribution
   inline double invcdfBW(double p, double mu, double gamma) {
     const double xn = std::tan(M_PI*(p-0.5));
     return gamma*xn + mu;
   }
 
   //@}
 
 
   /// @name Binning helper functions
   //@{
 
   /// @brief Make a list of @a nbins + 1 values equally spaced between @a start and @a end inclusive.
   ///
   /// NB. The arg ordering and the meaning of the nbins variable is "histogram-like",
   /// as opposed to the Numpy/Matlab version.
   inline vector<double> linspace(size_t nbins, double start, double end, bool include_end=true) {
     assert(end >= start);
     assert(nbins > 0);
     vector<double> rtn;
     const double interval = (end-start)/static_cast<double>(nbins);
     for (size_t i = 0; i < nbins; ++i) {
       rtn.push_back(start + i*interval);
     }
     assert(rtn.size() == nbins);
     if (include_end) rtn.push_back(end); //< exact end, not result of n * interval
     return rtn;
   }
 
 
   /// @brief Make a list of @a nbins + 1 values exponentially spaced between @a start and @a end inclusive.
   ///
   /// NB. The arg ordering and the meaning of the nbins variable is "histogram-like",
   /// as opposed to the Numpy/Matlab version, and the start and end arguments are expressed
   /// in "normal" space, rather than as the logarithms of the start/end values as in Numpy/Matlab.
   inline vector<double> logspace(size_t nbins, double start, double end, bool include_end=true) {
     assert(end >= start);
     assert(start > 0);
     assert(nbins > 0);
     const double logstart = std::log(start);
     const double logend = std::log(end);
     const vector<double> logvals = linspace(nbins, logstart, logend, false);
     assert(logvals.size() == nbins);
     vector<double> rtn; rtn.reserve(nbins+1);
     rtn.push_back(start); //< exact start, not exp(log(start))
     for (size_t i = 1; i < logvals.size(); ++i) {
       rtn.push_back(std::exp(logvals[i]));
     }
     assert(rtn.size() == nbins);
     if (include_end) rtn.push_back(end); //< exact end, not exp(n * loginterval)
     return rtn;
   }
 
 
   /// @brief Make a list of @a nbins + 1 values spaced for equal area
   /// Breit-Wigner binning between @a start and @a end inclusive. @a
   /// mu and @a gamma are the Breit-Wigner parameters.
   ///
   /// @note The arg ordering and the meaning of the nbins variable is "histogram-like",
   /// as opposed to the Numpy/Matlab version, and the start and end arguments are expressed
   /// in "normal" space.
   inline vector<double> bwspace(size_t nbins, double start, double end, double mu, double gamma) {
     assert(end >= start);
     assert(nbins > 0);
     const double pmin = cdfBW(start, mu, gamma);
     const double pmax = cdfBW(end,   mu, gamma);
     const vector<double> edges = linspace(nbins, pmin, pmax);
     assert(edges.size() == nbins+1);
     vector<double> rtn;
     for (double edge : edges) {
       rtn.push_back(invcdfBW(edge, mu, gamma));
     }
     assert(rtn.size() == nbins+1);
     return rtn;
   }
 
 
   /// @brief Return the bin index of the given value, @a val, given a vector of bin edges
   ///
   /// An underflow always returns -1. If allow_overflow is false (default) an overflow
   /// also returns -1, otherwise it returns the Nedge-1, the index of an inclusive bin
   /// starting at the last edge.
   ///
   /// @note The @a binedges vector must be sorted
   /// @todo Use std::common_type<NUM1, NUM2>::type x = val; ?
-  template <typename NUM1, typename NUM2>
-  inline typename std::enable_if<std::is_arithmetic<NUM1>::value && std::is_arithmetic<NUM2>::value, int>::type
-  binIndex(NUM1 val, const vector<NUM2>& binedges, bool allow_overflow=false) {
+  template <typename NUM, typename CONTAINER>
+  inline typename std::enable_if<std::is_arithmetic<NUM>::value && std::is_arithmetic<typename CONTAINER::value_type>::value, int>::type
+  binIndex(NUM val, const CONTAINER& binedges, bool allow_overflow=false) {
     if (val < binedges.front()) return -1; ///< Below/out of histo range
     if (val >= binedges.back()) return allow_overflow ? int(binedges.size())-1 : -1; ///< Above/out of histo range
-    return std::distance(binedges.begin(), --std::upper_bound(binedges.begin(), binedges.end(), val));
-    //
-    // int index = -1;
-    // for (size_t i = 1; i < binedges.size(); ++i) {
-    //   if (val < binedges[i]) {
-    //     index = i-1;
-    //     break;
-    //   }
-    // }
-    // assert(inRange(index, -1, int(binedges.size())-1));
-    // return index;
+    auto it = std::upper_bound(binedges.begin(), binedges.end(), val);
+    return std::distance(binedges.begin(), --it);
   }
 
   //@}
 
 
   /// @name Discrete statistics functions
   //@{
 
   /// Calculate the median of a sample
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
   median(const vector<NUM>& sample) {
     if (sample.empty()) throw RangeError("Can't compute median of an empty set");
     vector<NUM> tmp = sample;
     std::sort(tmp.begin(), tmp.end());
     const size_t imid = tmp.size()/2; // len1->idx0, len2->idx1, len3->idx1, len4->idx2, ...
     if (sample.size() % 2 == 0) return (tmp.at(imid-1) + tmp.at(imid)) / 2.0;
     else return tmp.at(imid);
   }
 
 
   /// Calculate the mean of a sample
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
   mean(const vector<NUM>& sample) {
     if (sample.empty()) throw RangeError("Can't compute mean of an empty set");
     double mean = 0.0;
     for (size_t i = 0; i < sample.size(); ++i) {
       mean += sample[i];
     }
     return mean/sample.size();
   }
 
   // Calculate the error on the mean, assuming Poissonian errors
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
   mean_err(const vector<NUM>& sample) {
     if (sample.empty()) throw RangeError("Can't compute mean_err of an empty set");
     double mean_e = 0.0;
     for (size_t i = 0; i < sample.size(); ++i) {
       mean_e += sqrt(sample[i]);
     }
     return mean_e/sample.size();
   }
 
 
   /// Calculate the covariance (variance) between two samples
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
   covariance(const vector<NUM>& sample1, const vector<NUM>& sample2) {
     if (sample1.empty() || sample2.empty()) throw RangeError("Can't compute covariance of an empty set");
     if (sample1.size() != sample2.size()) throw RangeError("Sizes of samples must be equal for covariance calculation");
     const double mean1 = mean(sample1);
     const double mean2 = mean(sample2);
     const size_t N = sample1.size();
     double cov = 0.0;
     for (size_t i = 0; i < N; i++) {
       const double cov_i = (sample1[i] - mean1)*(sample2[i] - mean2);
       cov += cov_i;
     }
     if (N > 1) return cov/(N-1);
     else return 0.0;
   }
 
   /// Calculate the error on the covariance (variance) of two samples, assuming poissonian errors
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
   covariance_err(const vector<NUM>& sample1, const vector<NUM>& sample2) {
     if (sample1.empty() || sample2.empty()) throw RangeError("Can't compute covariance_err of an empty set");
     if (sample1.size() != sample2.size()) throw RangeError("Sizes of samples must be equal for covariance_err calculation");
     const double mean1 = mean(sample1);
     const double mean2 = mean(sample2);
     const double mean1_e = mean_err(sample1);
     const double mean2_e = mean_err(sample2);
     const size_t N = sample1.size();
     double cov_e = 0.0;
     for (size_t i = 0; i < N; i++) {
       const double cov_i = (sqrt(sample1[i]) - mean1_e)*(sample2[i] - mean2) +
         (sample1[i] - mean1)*(sqrt(sample2[i]) - mean2_e);
       cov_e += cov_i;
     }
     if (N > 1) return cov_e/(N-1);
     else return 0.0;
   }
 
 
   /// Calculate the correlation strength between two samples
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
   correlation(const vector<NUM>& sample1, const vector<NUM>& sample2) {
     const double cov = covariance(sample1, sample2);
     const double var1 = covariance(sample1, sample1);
     const double var2 = covariance(sample2, sample2);
     const double correlation = cov/sqrt(var1*var2);
     const double corr_strength = correlation*sqrt(var2/var1);
     return corr_strength;
   }
 
   /// Calculate the error of the correlation strength between two samples assuming Poissonian errors
   /// @todo Support multiple container types via SFINAE
   template <typename NUM>
   inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
   correlation_err(const vector<NUM>& sample1, const vector<NUM>& sample2) {
     const double cov = covariance(sample1, sample2);
     const double var1 = covariance(sample1, sample1);
     const double var2 = covariance(sample2, sample2);
     const double cov_e = covariance_err(sample1, sample2);
     const double var1_e = covariance_err(sample1, sample1);
     const double var2_e = covariance_err(sample2, sample2);
 
     // Calculate the correlation
     const double correlation = cov/sqrt(var1*var2);
     // Calculate the error on the correlation
     const double correlation_err = cov_e/sqrt(var1*var2) -
       cov/(2*pow(3./2., var1*var2)) * (var1_e * var2 + var1 * var2_e);
 
     // Calculate the error on the correlation strength
     const double corr_strength_err = correlation_err*sqrt(var2/var1) +
       correlation/(2*sqrt(var2/var1)) * (var2_e/var1 - var2*var1_e/pow(2, var2));
 
     return corr_strength_err;
   }
 
   //@}
 
 
   /// @name Angle range mappings
   //@{
 
   /// @brief Reduce any number to the range [-2PI, 2PI]
   ///
   /// Achieved by repeated addition or subtraction of 2PI as required. Used to
   /// normalise angular measures.
   inline double _mapAngleM2PITo2Pi(double angle) {
     double rtn = fmod(angle, TWOPI);
     if (isZero(rtn)) return 0;
     assert(rtn >= -TWOPI && rtn <= TWOPI);
     return rtn;
   }
 
   /// Map an angle into the range (-PI, PI].
   inline double mapAngleMPiToPi(double angle) {
     double rtn = _mapAngleM2PITo2Pi(angle);
     if (isZero(rtn)) return 0;
     if (rtn > PI) rtn -= TWOPI;
     if (rtn <= -PI) rtn += TWOPI;
     assert(rtn > -PI && rtn <= PI);
     return rtn;
   }
 
   /// Map an angle into the range [0, 2PI).
   inline double mapAngle0To2Pi(double angle) {
     double rtn = _mapAngleM2PITo2Pi(angle);
     if (isZero(rtn)) return 0;
     if (rtn < 0) rtn += TWOPI;
     if (rtn == TWOPI) rtn = 0;
     assert(rtn >= 0 && rtn < TWOPI);
     return rtn;
   }
 
   /// Map an angle into the range [0, PI].
   inline double mapAngle0ToPi(double angle) {
     double rtn = fabs(mapAngleMPiToPi(angle));
     if (isZero(rtn)) return 0;
     assert(rtn > 0 && rtn <= PI);
     return rtn;
   }
 
   /// Map an angle into the enum-specified range.
   inline double mapAngle(double angle, PhiMapping mapping) {
     switch (mapping) {
     case MINUSPI_PLUSPI:
       return mapAngleMPiToPi(angle);
     case ZERO_2PI:
       return mapAngle0To2Pi(angle);
     case ZERO_PI:
       return mapAngle0To2Pi(angle);
     default:
       throw Rivet::UserError("The specified phi mapping scheme is not implemented");
     }
   }
 
   //@}
 
 
   /// @name Phase-space measure helpers
   //@{
 
   /// @brief Calculate the difference between two angles in radians
   ///
   /// Returns in the range [0, PI].
   inline double deltaPhi(double phi1, double phi2) {
     return mapAngle0ToPi(phi1 - phi2);
   }
 
   /// Calculate the abs difference between two pseudorapidities
   ///
   /// @note Just a cosmetic name for analysis code clarity.
   inline double deltaEta(double eta1, double eta2) {
     return fabs(eta1 - eta2);
   }
 
   /// Calculate the abs difference between two rapidities
   ///
   /// @note Just a cosmetic name for analysis code clarity.
   inline double deltaRap(double y1, double y2) {
     return fabs(y1 - y2);
   }
 
   /// Calculate the distance between two points in 2D rapidity-azimuthal
   /// ("\f$ \eta-\phi \f$") space. The phi values are given in radians.
   inline double deltaR(double rap1, double phi1, double rap2, double phi2) {
     const double dphi = deltaPhi(phi1, phi2);
     return sqrt( sqr(rap1-rap2) + sqr(dphi) );
   }
 
   /// Calculate a rapidity value from the supplied energy @a E and longitudinal momentum @a pz.
   inline double rapidity(double E, double pz) {
     if (isZero(E - pz)) {
       throw std::runtime_error("Divergent positive rapidity");
       return MAXDOUBLE;
     }
     if (isZero(E + pz)) {
       throw std::runtime_error("Divergent negative rapidity");
       return -MAXDOUBLE;
     }
     return 0.5*log((E+pz)/(E-pz));
   }
 
   //@}
 
 
 }
 
 
 #endif
diff --git a/include/Rivet/Tools/TypeTraits.hh b/include/Rivet/Tools/TypeTraits.hh
--- a/include/Rivet/Tools/TypeTraits.hh
+++ b/include/Rivet/Tools/TypeTraits.hh
@@ -1,66 +1,66 @@
 // -*- C++ -*-
 #ifndef RIVET_TypeTraits_HH
 #define RIVET_TypeTraits_HH
 
 #include <type_traits>
 
 namespace Rivet {
 
   /// Mechanisms to allow references and pointers to templated types
   /// to be distinguished from one another (since C++ doesn't allow
   /// partial template specialisation for functions.
   /// Traits methods use specialisation of class/struct templates, and
   /// some trickery with typedefs and static const integral types (or
   /// enums) to implement partial function specialisation as a work-around.
 
   /// @cond INTERNAL
 
   namespace SFINAE {
     /// C++11 equivalent of C++17 std::void_t
     template <typename ...>
     using void_t = void;
   }
 
 
   struct RefType { };
 
   struct PtrType { };
 
   template <typename T>
   struct TypeTraits;
 
   template <typename U>
   struct TypeTraits<const U&> {
     typedef RefType ArgType;
   };
 
   template <typename U>
   struct TypeTraits<const U*> {
     typedef PtrType ArgType;
   };
 
 
 
   /// SFINAE definition of dereferenceability trait, cf. Boost has_dereference
   template <typename T, typename=void>
   struct Derefable : std::false_type {};
   //
   template <typename T>
   struct Derefable<T, SFINAE::void_t< decltype(*std::declval<T>())> > : std::true_type {};
 
 
   /// SFINAE check for non-const iterability trait
   // template <typename T, typename=void>
   // struct Iterable : std::false_type {};
   // //
   // template <typename T>
   // struct Iterable<T, SFINAE::void_t< decltype(*std::declval<T>())> > : std::true_type {};
-  template <T>
+  template <typename T>
   using ConstIterable = pretty_print::is_container<T>
 
 
   /// @endcond
 
 }
 
 #endif