c++ template parameter type inference
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 likeC<&X::f>
instead of having to sayC<decltype(&X::f), &X::f>
.The basic idea is to be able to say
template<using typename T, T t> struct C {/* ... */};
to indicate thatT
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>”
idiomtemplate<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;
How to infer a function type parameter in a template function with a lambda passed as argument?
The actual problem is that a lambda function has it own type, that cannot be reduced to R(&)(T)
. Because of that, C<T>
is an incomplete type as correctly outlined by the compiler.
As long as you use non-capturing lambdas, you can rely on the fact that they decay to function pointers and do this:
auto p3 = makeC(*(+[](int a){return a;}));
Or this:
template<typename T>
auto makeC(T&& fun) -> C<decltype(*(+std::forward<T>(fun)))> {
return C<decltype(*(+std::forward<T>(fun)))>(std::forward<T>(fun));
}
Another possible solution that works with capturing lambdas is this:
#include <utility>
#include <string>
template<typename T>
class C: T {
template<typename F>
C(F&& fun): T{std::forward<F>(fun)} {}
};
template<typename R, typename T>
class C<R(&)(T)> {
public:
template<typename F>
C(F&& fun) {}
};
template<typename T>
C<T> makeC(T&& fun) {
return C<T>(std::forward<T>(fun));
}
int foo(int a){return a;}
int main() {
auto p1 = makeC(foo);
auto p2 = C<int(&)(int)>([](int a){return a;});
auto p3 = makeC([](int a){return a;});
}
This way, when dealing with a lambda, C
actually inherits from it and privately contains its operator()
.
Template type inference using std::views
This has nothing to do with views. You can reduce the problem to:
template <typename T>
int length(T const& x) { return x.length(); }
template <typename F>
void do_something(F&& f) {
// in theory use f to call something
}
void stuff() {
do_something(length); // error
}
C++ doesn't really do type inference. When you have do_something(length)
, we need to pick which length
we're talking about right there. And we can't do that, so it's an error. There's no way for do_something
to say "I want the instantiation of the function template that will be called with a std::string
- it's entirely up to the caller to give do_something
the right thing.
The same is true in the original example. length<E>
is a concrete function. length
is not something that you can just pass in.
The typical approach is to delay instantiation by wrapping your function template in a lambda:
void stuff() {
do_something([](auto const& e) { return length(e); }); // ok
}
Now, this works - because a lambda is an expression that has a type that can be deduced by do_something
, while just length
is not. And we don't have to manually provide the template parameter, which is error prone.
We can generalize this with a macro:
#define FWD(arg) static_cast<decltype(arg)&&>(arg)
#define LIFT(name) [&](auto&&... args) -> decltype(name(FWD(args)...)) { return name(FWD(args)...); }
void stuff() {
do_something(LIFT(length));
}
Which avoids some extra typing and probably makes the intent a little clearer.
Template function default parameter and type inference
Your examples don't work because template argument deduction fails for foo()
. With C++11 you're allowed to specify default template arguments for function templates, so you change the definition to
template<typename T = void*> void foo(T par = nullptr) {return;}
With C++03 I don't know of any way other than to explicitly specify the template argument.
The reason the template argument is not deduced from the default argument is because the standard states that is a non-deduced context.
From N3691, §14.8.2.5/5
The non-deduced contexts are:
...
— A template parameter used in the parameter type of a
function parameter that has a default argument that is being used in
the call for which argument deduction is being done.
When a compiler can infer a template parameter?
Template parameters can be inferred for function templates when the parameter type can be deduced from the template parameters
So it can be inferred here:
template <typename T>
void f(T t);
template <typename T>
void f(std::vector<T> v);
but not here:
template <typename T>
T f() {
return T();
}
And not in class templates.
So the usual solution to your problem is to create a wrapper function, similar to the standard library function std::make_pair
:
template <class T>
class MyClass {
public:
MyClass(T t) {}
void print(){
std::cout<<"try MyClass"<<std::endl;
}
};
template <typename T>
MyClass<T> MakeMyClass(T t) { return MyClass<T>(t); }
and then call auto a = MakeMyClass(5);
to instantiate the class.
Related Topics
How to Declare an Array of Objects Whose Class Has No Default Constructor
Get Function Names from Call Stack
Why Is "Operator Void" Not Invoked with Cast Syntax
Where in Qt Creator Do I Pass Arguments to a Compiler
Ternary Conditional and Assignment Operator Precedence
Waiting Thread Until a Condition Has Been Occurred
How to Get the Application Data Path in Windows Using C++
Constexpr Function Parameters as Template Arguments
Does Resizing a Vector Invalidate Iterators
Using C Function from Other Package in Rcpp
What Exactly Are C++ Definitions, Declarations and Assignments
Finding Nearest Point in an Efficient Way
How to Enable Visual Styles Without a Manifest
Initializing a C++ Std::Istringstream from an in Memory Buffer