Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to bind a template<template> parameter?

Context

I have a custom comparator that takes another comparator and applies an additional check:

template <template <typename> class Comparator, typename T>
struct SoftOrder : public std::binary_function<T, T, bool> {
    bool operator()(const T lhs, const T rhs) const {
        return Comparator<T>()(lhs, rhs) && AnotherCheck();
    }
};

I have a second class that accepts a comparator, e.g.:

template <template <typename> class Comparator>
class Processor { ... };

It is easy to instantiate a Processor with a standard comparator (e.g. std::less) like so:

Processor<std::less> processor1;
Processor<std::greater> processor2;

However it is not so easy to instantiate with SoftOrder as the compiler correctly complains about the missing second template argument:

Processor<SoftOrder<std::less> > processor3; // <-- Fails to compile

Current Solutions

I have come up with a few solutions prior to posting this question.

First Solution - Lots of Derived Classes

template <typename T>
struct SoftOrderLessThan : public SoftOrder<std::less, T> {};

template <typename T>
struct SoftOrderGreaterThan : public SoftOrder<std::greater, T> {};

The main drawback of this solution is the need to create a new struct every time a new variant is required, e.g.:

template <typename T>
struct SoftOrderLessThan : public SoftOrder<std::less, T> {}; // Never used after the next line.
Processor<SoftOrderLessThan> processor3;

Second Solution - A very specific bind class

template <template <typename> class Comparator>
struct BindToSoftOrder {
    template <typename T>
    struct type : public SoftOrder<Comparator, T> {};
};

This is slightly better in that we don't need to create the intermediate classes explicitly:

Processor<BindToSoftOrder<std::less>::type> processor3;

The downside is the requirement of a class specialised for this situation which cannot really be generalised by making SoftOrder a template parameter on BindToSoftOrder as this would make it a template<template<template>>> which is not permitted by the standard.

Third Solution - C++11 template aliases

template <typename T>
using SoftOrderLessThan = SoftOrder<std::less, T>;

Nicer than the first option in that it doesn't require the introduction of new classes, however still requires littering the code with this extra code that is only used in passing onwards to another template class:

template <typename T>
using SoftOrderLessThan = SoftOrder<std::less, T>; // Never used again
Processor<SoftOrderLessThan> processor3;

Finally, the question

Is there a generic way to bind my custom comparator to a specific comparator in the following manner?

Processor<SomeCoolMetaTemplateBind<SoftOrder, std::less>::type> processor3;

I believe if all of the template parameters were simple types I could just do something like Processor<boost::mpl::bind<SoftOrder, std::less> >, but the presence of the template type in the template parameter list prevents this from occurring.

An ideal solution would involve C++03, but am happy to hear C++11 solutions as well.

If it's not possible, I hope at least the question was interesting.

like image 773
Shane Avatar asked Jul 07 '12 17:07

Shane


People also ask

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

Can template have default parameters?

Template parameters may have default arguments. The set of default template arguments accumulates over all declarations of a given template.

Why do we use template template parameter?

8. Why we use :: template-template parameter? Explanation: It is used to adapt a policy into binary ones.

What are non-type parameters for templates?

A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument. A non-type parameter can be any of the following types: An integral type. An enumeration type.


1 Answers

Seems like this would work:

template <
  template <template <typename> class,class> class U,
  template <typename> class X
>
struct SomeCoolMetaTemplateBind {
  template <typename T>
  struct type : public U<X,T> {
  };
};
like image 173
Vaughn Cato Avatar answered Oct 02 '22 14:10

Vaughn Cato