Lets assume I have a template function
template <typename T>
void do_sth(T const&);
For some types ("small" and copyable) it would be better to pass an argument by value instead of reference.
So my question is: What is the simplest way to overload a function depending on the underlying type?
Remark:
Best what I came to is using enable_if with some conditions of "simple" type. And I believe there's no such a type trait as "simple type" in the standard library. Correct me if I'm wrong. Moreover: Using enable_if gets complicated as a function takes more template arguments (edited) because template <typename T, typename U> void do_sth(T, U) would need 4 overloads: (value, value), (value, ref), (ref, value) and (ref, ref).
Don't do this. Template functions are inlined at the drop of a hat, and a reference to an x is an alias with no identity once the function is inlined.
Encourage the function to be inlined instead of doing a mess of SFINAE, unless and until you have proven this to be an important bottleneck.
After discovering this function is taking up more time than anything else you can optimize, test its improvement by manualy writing the by value version in a couple of key cases to ensure you actually get a benefit (I doubt you will).
boost has call_traits:
template <typename T>
void do_sth(typename boost::call_traits<T>::param_type)
But one big issue is that T is no longer deducible
(and so you have to call it do_sth<int>(42) or do_sth<MyBigObj>(myBigObj)).
So might be used for non template methods in template class:
template <typename T>
struct S
{
void do_sth(typename boost::call_traits<T>::param_type);
};
But anyway, chances are that compiler actually inlines code, resulting in same generated code.
Related
I have a function that needs to be able to work for different container types, for example
void foo(const std::vector<bar>& param1, const std::vector<double>& params2)
and
void foo(const std::list<bar>& param1, const std::list<double>& params2)
where bar is a class that I've written. The function body itself uses generic C++ standard library functions.
Is there a way I can templatise this? I have tried
template<typename T> void foo(const T<bar>&, const T<double>&)
But this gives the compiler error
error C2988: unrecognizable template declaration/definition
I'm using MSVC2015.
You should declare T as template template parameter to indicate that it's a template-name (and needs arguments to be instantiated), e.g.
template<template <typename...> class T>
void foo(const T<bar>&, const T<double>&);
LIVE
There are basically three solutions:
Use template template arguments.
Use iterators (which is what the standard library itself uses)
Use different templates for different arguments, and let the compiler deduce the actual types. With your function it would be something like
template<typename T, typename U> void foo(const T&, const U&);
Solution 2 is the recommended one if you just need to iterate over the contents of the container. Solution 3 is the recommended one if you need access tot he container itself.
So-called template template arguments will allow you to write the desired code (as also shown in other answers or over on CppReference).
Alternatively, you could also write generic functions that accept iterators. This is exactly how the functions in the <algorithm> are implemented. I tend to believe that functions that accept iterators are actually more flexible as the caller can decide what range needs to be processed by the function.
I can infer template parameters with function parameters
template <typename T>
void f(T a)
{ /* */ }
f(4); // T inferred to be `int`
However I can't use function parameters to infer template parameters, which aren't types:
template <int I>
void g(int I) // error: declaration of 'I' shadows template parameter
{ /* */ }
Let's say I want to write a function divive_dy_2(int number) and I want to make sure number is not zero.
I've seen a very simple way to do this, which would require me to pass the argument to a template parameter, which then would use SFINAE to disable it, if it was 0.
But it feels counter-intuitive to expose something like this in an API. Is there some workaround?
(And yes, I could use exceptions and what not, what would probably even be a better idea, but I'm learning SFINAE at the moment and want to know where it's limits are)
If your argument is known at compile time, you do not even need sfinae for your check - a simple assert will provide much better diagnostics.
However, if the argument is not known (which is more likely scenario) than you can not make it template argument, and you have to use exceptions or return error code to indicate that provided argument is outside of supported domain.
Think of template as smth the same as #define
If you want to use 4 as template argument, it's roughly
template <int I>
void g()
{ ... here use I as _value_, not type}
You can quickly look in Alexandrescu's C++ book for a longer discussion, how to use values in template arguments.
I was reading book "template compelte guide" , I section : 12.2.1 Signatures I couldn't understand a sentence of author :
Its return type, if the function is generated from a function template
what does the author means by "function generated by function template" ? , is he talking about template-id here? if so, why does return type matters in the case, since signature is defined by us?
A trivial example will be helpful, thanks.
In section 12.2.1 the authors describe under which situations declarations of functions can coexist. The declaration of function templates can coexist even if they have a different return type, e.g., you can declare:
template <typename T> int f();
tepmlate <typename T> char f();
If you make these functions non-templates, you can't even declare them. Of course, in the form above you won't be able to call the function although you may be able to explicitly the select one of the functions using a cast (I'm not sure about this).
The primary use of having functions with different return types coexist (although this wasn't the original intent), is to remove some of these functions from the overload set based on condition, e.g.:
template <typename T>
typename std::enable_if<std::numeric_limits<T>::is_specialized, T>::type
f(T); // used for types for which std::numeric_limits<T> is specialied
template <typename T>
typename std::enable_if<!std::numeric_limits<T>::is_specialized, T>::type
f(T); // used for types for which std::numeric_limits<T> is not specialied
I wonder why template specializations make sense?
Aren't the following things equivalent?
Template specialization:
template <typename T>
void f(T t) {
something(t);
}
template <>
void f<int>(int t) {
somethingelse(t);
}
Non-template function instead of specialization:
void f(int t) {
somethingelse(t);
}
I believe these are the same because the non-template function will always be preferred.
This is the answer I came up with:
It's different if the template parameter is not a parameter of the function being defined:
template <typename T>
void f() {
T t;
something(t);
}
template <>
void f<int>() {
int t;
somethingelse(t);
}
In this case defining:
void f() {
int t;
somethingelse(t);
}
would make all the template versions unuseable.
Maybe somebody else has better ideas. :)
The question boils down to determining when the specialization will be used that the overload cannot. There are different situations where this is the case although they are uncommon enough, and it is simple enough to make mistakes that the general recommendation is to prefer overloads to specializations.
When the caller explicitly requests the use of a template. In the code example you provide if the call is f<int>(42) or even f<42>(), then the overload will not be used.
When you cannot provide the required overloads, or the overload cannot be resolved at the place of call. For example if the type is not one of the function arguments (it is either not present in the signature at all or only in the return type:
template
T f();
In this case, you cannot provide overloads int f(); and double f(); but you can provide as many template specializations as you need, and it will be up to the user to force the selection of one or the other. Note that this could be considered a subcase of the previous case: because the template arguments take no part in the function arguments, the user needs to provide the template arguments, so the call is explicitly to a template.
When you want to place special constraints on the combination of arguments and inhibit implicit conversions:
template
void f( T, T ); // Both argument must be the same type
Because template argument deduction only perform perfect matches, this template can only be used when both arguments are of the exact same type, if you add an overload void f(int,int) that overload can be used with any combination of types that are implicitly convertible to int, like f( 5, 3.0 ), but the specialization won't.
In general, for most cases, none of the cases above really apply, so an overload should be preferred.
There might be more, but those are the ones I can recall off the top of my head
The way you declare the function does matter if you insist on calling it like f<int>(42). This will find the specialization, but not the overload.
If the call always looks like f(42), either alternative will work.
Function template specialization is deprecated in favor of function overloads with one exception: you are allowed to add a function template specialization to the std namespace, you aren't allowed to add a new function. So, if you need to supply a specific version for something in the std namespace, you have to use template specialization. For instance, to support creating an unordered_map with a user-defined class as the key you have to specialize std::hash for your class.
Look at the following test code:
template<class T> struct Wrap {};
template<typename T> inline
void fun (T *&Int) // **choice 1**
{}
template<typename T> inline
void fun (Wrap<T> *&Int) // **choice 2**
{}
int main()
{
int i = 6;
fun((char*&)(i)); // **call 1**
fun((Wrap<char>*&)(i)); // **call 2**
}
When I run this code in linux g++, it works as per expectations. When fun() is called with char*&, it calls function of choice 1 straight forward.
However, I am interested when we call fun() with Wrap< char >*& and it calls the choice 2. Even though choice 1 and 2 both appear valid for the 2nd call, compiler manages to choose little better contender -> choice 2 (because it exists).
Question: Is it guaranteed that, the same behavior will be retained for any other compiler for C++ ? If not, then is there any other alternative to make it deterministic?
The second choice is chosen because it's more specialized than the first- that is, T*& can bind to any non-temporary T*, but Wrap<T>*& can only bind to a non-temporary Wrap<T>*. This is Standard as far as I know and should be portable behaviour but what is and isn't portable in practice when it comes to this sort of thing is often not the definition of what's Standard.
Someone with a better knowledge of the spec can confirm this, but I believe that as Wrap<T> is a more specific type than simply T, call 2 will always resolve to 'choice 2', on all platforms compilers.
While the code might look like a template specialization, that is not the case. The language does not allow for partial template function specializations. The two are unrelated templates that happen to be overloads.
The compiler will lookup up the call to fun( (Wrap<char>*&) i ) with the usual lookup mechanisms, will find the two templates and will determine that there are two potential overloads:
template <typename T> void fun( T*& ); // with T == Wrap<char>
template <typename T> void fun( Wrap<T>*& ) // with T == char
Overload resolution will then determine that the second is a better match and instantiate it. This is guaranteed by the standard, but beware: they are not the same template, but rather different templates and you might run into undexpected results. Look at the article #LiKao linked for more insight.
One thing that might make this even more problematic is, that the rules for specializing template classes and template functions differ a lot. This is do to the possibility of overloading template functions while there is no possibility of overloading classes. Because I am not so firm on this topic I will just link to someone who is able to explain it in more depth:
Herb Sutter: "Why Not Specialize Function Templates?"