I'm trying to write something that is similar to std::bind
, but for template types instead of objects. Effectively, I would like to bind one template parameter to a template type. (In a sort of similar way, in std::bind
we bind one (or more) real parameters to a function-ish object.)
What I want is best illustrated by the following C++ code:
#include <tuple>
#include <type_traits>
using namespace std;
/* This struct should be in some header-only utility library */
template <template <typename...> typename Template, typename T>
struct template_bind {
template <typename... Args>
using template_type = Template<T, Args...>;
};
/* This class should be in a separate header file */
// If we change the following line to add '...' then it works
// template <template <typename...> typename Template>
template <template <typename> typename Template>
class my_complicated_class {
public:
/* stuff */
private:
Template<float> data_;
};
/* This probably would be in a third file that includes both headers */
int main()
{
using t1 = template_bind<std::tuple, int>;
using t2 = template_bind<t1::template_type, double>;
my_complicated_class<t2::template_type> stuff_with_tuple; // Compile error
using p1 = template_bind<std::pair, int>;
my_complicated_class<p1::template_type> stuff_with_pair; // Compile error
}
Interestingly, the code compiles on GCC and MSVC with C++17, but does not compile on Clang (with whatever C++ standard) or GCC/MSVC with C++14. The error (on those compilers that won't compile it) is that my_complicated_class
requires a template template parameter that takes in a single template parameter, but template_type
is a variadic template.
Changing my_complicated_class
to accept a variadic template template parameter fixes the problem on all compilers. However, it would feel weird to change my_complicated_class
to accept a variadic template template parameter, because my_complicated_class
probably shouldn't know anything about the template template parameter that is used. (Otherwise, an argument could be made that all template template parameters should be written as variadic templates, e.g. template <template <typename...> typename Template>
, but that does not seem to be way template template parameters are usually written.)
Which compiler(s) is/are non-conformant, and how can I make the code compile on a C++14 compiler?
Clang does not support C++17 template template argument/parameter matching.
The relevant paragraph:
A template-argument matches a template template-parameter (call it P) when each of the template parameters in the template-parameter-list of the template-argument's corresponding class template or alias template (call it A) matches the corresponding template parameter in the template-parameter-list of P. [...]
A template-argument matches a template template-parameter P when P is at least as specialized as the template-argument A. [...]
This is this last sentence that makes your code well-formed in C++17. When P is template<class> class
and A is template<class...> class
, P is more specialized than A.