Reputation: 131656
In C++, I'd like to be able to say:
template <typename T> {
void foo(T t1);
void bar(T t1, T t2);
}
i.e. have the template <typename T>
apply to multiple definitions/declarations. The { }
syntax in the above doesn't compile; is there another way to express the same?
Note: Wrapping the functions in a struct/class won't do, I want something that's both terse and without any dummy entities.
Upvotes: 3
Views: 252
Reputation: 5518
If you can wait for C++17 concepts, you could use auto
parameter types instead of using templates.
Say I have the following C++17 function:
auto func(auto x, auto y){
return x + y;
}
This could be re-written in C++14 like this:
template<typename T, typename U>
auto func(T x, U y){
return x + y;
}
Which could be re-written again in C++11 like this:
template<typename T, typename U>
auto func(T x, U y) -> typename std::decay<decltype(x + y)>::type{
return x + y;
}
as @T.C. stated in the comments, the std::decay
is necessary here because the semantics of auto return type in c++14 will decay the final statement whereas decltype(x + y)
will not decay the expressions type. If we wanted to not decay the return expressions type we would write:
// template here for c++14
decltype(auto) func(auto x, auto y){
return x + y;
}
So, given this information we could re-write your functions like this:
void foo(auto t1);
void bar(auto t1, auto t2);
Remembering that if we ever need to reference the types of the arguments we will need to use decltype
.
If you intend on using the types extensively templates are a much better idea, but to keep the brevity of the function declaration in the function definition we could write some using
statements for the types:
void bar(auto t1, auto t2){
using type_t1 = std::decay_t<decltype(t1)>;
using type_t2 = std::decay_t<decltype(t2)>;
}
The need for std::decay_t
in this situation arises if we pass in a pointer or reference with some cv qualifier. We only want the underlying type.
Then to enforce that the underlying types of type_t1
and type_t2
are the same as in your example we could use SFINAE
to exclude any instantiation of bar
where they are not:
auto bar(auto t1, auto t2) -> std::enable_if_t<std::is_same<std::decay_t<decltype(t1)>, std::decay_t<decltype(t2)>>::value>{
using type = std::decay_t<decltype(t1)>;
}
again, decaying the type so that if we get pointers we are comparing the underlying type.
Just remember to ask yourself if this is all worth it just to not have to write template<typename T>
a few more times.
Upvotes: 4