c++ template parameter type inference

2020-01-27 08:04发布

问题:

I have such a template in C++

template<typename T, T* P> struct Ptr {};

so I can use it as such:

const int i = 0;
Ptr<int, &i> ptr;

or

Ptr<decltype(i), &i> ptr;

But I don't want to specify the type int or identity i twice, I want to use just

Ptr<&i> ptr;

and let the compiler figure out the int type part by itself.

How can I declare my template to do that ?

I've read this question but the answer is using macros, that's not nice: template of template c++?

can I do this by just template without macros ? I'm using Visual C++ 2013.

回答1:

UPDATE

c++17 introduced "P0127R2 Declaring non-type template parameters with auto", allowing to declare a non-type template parameter(s) with auto as a placeholder for the actual type:

template <auto P> struct Ptr {};

That is, P is a non-type template parameter. Its type can be inferred with decltype(P).

auto in a template parameter list is subject to well-known deduction and partial ordering rules. In your case, the type can be constrained to accept pointers only:

template <auto* P> struct Ptr {};

Note that the syntax utilizing auto is sufficient even for more detailed inspection, e.g.:

template <typename F>
struct FunctionBase;

template <typename R, typename... Args>
struct FunctionBase<R(*)(Args...)> {};

template <auto F>
struct Function : FunctionBase<decltype(F)> {};

It's also possible to use the inferred type as a contraint for other template parameters:

template <auto I, decltype(I)... Is>
struct List {};

Old answer

Since you are asking about a pure class template-based solution without the help of macro definitions then the answer is simple: as for now (Dec 2014, c++14) it is not possible.

This issue has been already identified by the WG21 C++ Standard Committee as a need and there are several proposals to let templates automatically infer the type of non-type template arguments.

The closest is N3601 Implicit template parameters:

Implicit template parameters

The purpose of this example is to eliminate the need for the redundant template<typename T, T t> idiom. This idiom is widely used, with over 100k hits on Google.

The goal is to be able to replace a template declaration like template<typename T, T t> struct C; with another declaration so that we can instantatiate the template like C<&X::f> instead of having to say C<decltype(&X::f), &X::f>.

The basic idea is to be able to say template<using typename T, T t> struct C {/* ... */}; to indicate that T should be deduced. To describe in more detail, we consider some extended examples of template classes and functions.

[...]

The key idea is that passing the type of the second template parameter is redundant information because it can be inferred using ordinary type deduction from the second type parameter. With that in mind, we propose that prefacing a template parameter with using indicates that it should not be passed explicitly as a template argument but instead will be deduced from subsequent non-type template arguments. This immediately allows us to improve the usability of describe_field as follows.

template<using typename T, T t> struct describe_field { /* ... */ };
/* ... */
cout << describe_field<&A::f>::name;   // OK. T is void(A::*)(int)
cout << describe_field<&A::g>::arity;  // OK. T is double(A::*)(size_t)

A similar proposal is the one included in N3405 Template Tidbits:

T for two

The motivating example is a putative reflection type trait giving properties of a class member.

struct A {
  void f(int i);
  double g(size_t s);
};
/* ... */
cout << describe<&A::f>::name;   // Prints "f"
cout << describe<&A::g>::arity;  // prints 1

The question is "what should the declaration of describe look like?" Since it takes a non-type template parameter, we need to specify the type of the parameter using the familiar (100k hits on Google) “template<class T, T t>” idiom

template<typename T, T t> struct describe;

[...]

Our key idea is that passing the type of the second template parameter is (nearly always) redundant information because it can be inferred using ordinary type deduction from the second type parameter. With that in mind, we propose allowing describe to be declared as follows.

template<typename T t> struct describe;
/* ... */
cout << describe<&A::f>::name;   // OK. T is void(A::*)(int)
cout << describe<&A::g>::arity;  // OK. T is double(A::*)(size_t)

The current status of both proposals can be tracked under EWG issue 9.

There are some other discussions proposing alternative syntax with auto:

template <auto T> struct describe;