Can the auto keyword in C++11 replace function templates and specializations? If yes, what are the advantages of using template functions and specializations over simply typing a function parameter as auto?
template <typename T>
void myFunction(T &arg)
{
// ~
}
vs.
void myFunction(auto &arg)
{
// ~
}
In a nutshell, auto cannot be used in an effort to omit the actual types of function arguments, so stick with function templates and/or overloads. auto is legally used to automatically deduce the types of variables:
auto i=5;
Be very careful to understand the difference between the following, however:
auto x=...
auto &x=...
const auto &x=...
auto *px=...; // vs auto px=... (They are equivalent assuming what is being
// assigned can be deduced to an actual pointer.)
// etc...
It is also used for suffix return types:
template <typename T, typename U>
auto sum(const T &t, const U &u) -> decltype(t+u)
{
return t+u;
}
Can the auto keyword in C++11 replace function templates and specializations?
No. There are proposals to use the keyword for this purpose, but it's not in C++11, and I think C++14 will only allow it for polymorphic lambdas, not function templates.
If yes, What are the advantages of using template functions and specializations over simply typing a function parameter as auto.
You might still want a named template parameter if you want to refer to the type; that would be more convenient than std::remove_reference<decltype(arg)>::type or whatever.
The only thing which makes it the auto keyword different from template that is you cannot make a generic class using the auto keyword.
class B { auto a; auto b; }
When you create an object of the above class it will give you an error.
B b; // Give you an error because compiler cannot decide type so it can not be assigned default value to properties
Whereas using template you can make a generic class like this:
template <class T>
class B {
T a;
};
void main() {
B<int> b; //No Error
}
Related
I'm currently using a C library that defines a number of data types, all of which need to have their lifetimes managed by the user. There are a number of functions defined in this fashion:
int* create() {
return new int();
}
void destroy(int* i) {
delete i;
}
Most of these don't need to be accessed after creation. They simply need to exist. Because of this, I'm trying to manage them using unique_ptr's declared in the scope of which I need them to live.
This is how such a declaration looks like:
// Note that I'm avoiding writing the type's name manually.
auto a = std::unique_ptr<std::decay_t<decltype(*create())>, decltype(&destroy)>{create(), &destroy};
But this is overly verbose, so I encapsulated it in a utility function template:
template<typename T>
auto make_unique_ptr(T* p, void (*f)(T*)) {
return std::unique_ptr<T, decltype(f)>(p, f);
}
Which is used this way:
auto b = make_unique_ptr(create(), &destroy);
This looks nice, but introduces a non-standard function that has no real purpose other than being syntactic sugar for some declarations. My colleagues may not even know it exists and end up creating other versions of it with different names.
c++17 Introduced class template argument deduction. I thought this is the perfect solution to my problem: A standard way to deduce all those types without resorting to user-defined wrappers. So I tried this:
auto c = std::unique_ptr{create(), &destroy};
As is the rule with C++ compilers and templates, this fails with a several lines long error message. Here are the relevant parts:
(...): error: class template argument deduction failed:
auto c = std::unique_ptr{create(), &destroy};
^
(...): note: candidate: 'template<class _Tp, class _Dp>
unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::remove_reference<_Dp>::type&&)-> std::unique_ptr<_Tp, _Dp>'
unique_ptr(pointer __p,
^~~~~~~~~~
(...): note: template argument deduction/substitution failed:
(...): note: couldn't deduce template parameter '_Tp'
auto c = std::unique_ptr{create(), &destroy};
^
Theoretically, I could add a deduction guide to handle this:
namespace std {
template<typename T>
unique_ptr(T* p, void (*f)(T*)) -> unique_ptr<T, decltype(f)>;
}
And it does work at least on my version of gcc, but the standard doesn't like it very much:
[namespace.std]
1 Unless otherwise specified, the behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std.
4 The behavior of a C++ program is undefined if it declares
(...)
4.4 - a deduction guide for any standard library class template.
There's also some problems related to differentiating between pointers and arrays, but let's ignore that.
Finally, the question(s): Is there any other way to 'help' std::unique_ptr (or maybe std::make_unique) to deduce the correct type when using custom deleters? Just in case this is an XY problem, is there any solutions I didn't think of for lifetime management of those types (perhaps std::shared_ptr)? If both of those answers are a negative, is there any improvements on c++20 I should be looking forward to, that solve this issue?
Feel free to test the above examples at Coliru.
I would recommend writing custom deleters rather than using function pointers. Using the function pointer would make all the unique_ptrs twice the size for no reason.
Instead, write a deleter templated on a function pointer:
template <auto deleter_f>
struct Deleter {
template <typename T>
void operator()(T* ptr) const
{
deleter_f(ptr);
}
};
Or, as Yakk - Adam Nevraumont mentioned in the comments:
template <auto deleter_f>
using Deleter = std::integral_constant<std::decay_t<decltype(deleter_f)>, deleter_f>;
Using it becomes pretty clean:
auto a = std::unique_ptr<int, Deleter<destroy>>{create()};
Although you might want to combine this with your make_unique_ptr function:
template <auto deleter_f, typename T>
auto create_unique_ptr(T* ptr)
{
return std::unique_ptr<T, Deleter<deleter_f>>{ptr};
}
// Usage:
auto a = create_unique_ptr<destroy>(create());
A type as a value:
template<class T>
struct tag_t {};
template<class T>
constexpr tag_t<T> tag{};
A value as a type:
template<auto f>
using val_t = std::integral_constant<std::decay_t<decltype(f)>, f>;
template<auto f>
constexpr val_t<f> val{};
Note that val<some_function> is an empty type that can be invoked in a constexpr context with () and it will call some_function. It can also store ints or whatever, but we are going to use it to store function pointers statelessly.
Now let's have fun:
namespace factory {
// This is an ADL helper that takes a tag of a type
// and returns a function object that can be used
// to allocate an object of type T.
template<class T>
constexpr auto creator( tag_t<T> ) {
return [](auto&&...args){
return new T{ decltype(args)(args)... };
};
}
// this is an ADL helper that takes a tag of a type
// and returns a function object that can be used
// to destroy that type
template<class T>
constexpr auto destroyer( tag_t<T> ) {
return std::default_delete<T>{};
}
// This is a replacement for `std::unique_ptr`
// that automatically finds the destroying function
// object using ADL-lookup of `destroyer(tag<T>)`.
template<class T>
using unique_ptr = std::unique_ptr< T, decltype(destroyer(tag<T>)) >; // ADL magic here
// This is a replacement for std::make_unique
// that uses `creator` and `destroyer` to find
// function objects to allocate and clean up
// instances of T.
template<class T, class...Args>
unique_ptr<T> make_unique(Args&&...args) {
// ADL magic here:
return unique_ptr<T>( creator( tag<T> )(std::forward<Args>(args)...) );
}
}
ok, that is a framework.
Now let's imagine you have some library. It has a type in it. It needs super secret special sauce to create and destroy instances of it here:
namespace some_ns {
struct some_type {
int x;
};
some_type* create( int a, int b ) {
return new some_type{ a+b }; // ooo secret
}
void destroy( some_type* foo ) {
delete foo; // ooo special
}
}
and we want to connect it up. You reopen the namespace:
namespace some_ns {
constexpr auto creator( tag_t<some_type> ) {
return val<create>;
}
constexpr auto destoyer( tag_t<some_type> ) {
return val<destroy>;
}
}
and we are done.
factory::unique_ptr<some_ns::some_type> is the right type to store a unique ptr to some_type. To create it you just factory::make_unique<some_ns::some_type>( 7, 2 ) and you get a unique ptr of the correct type with a destroyer lined up, with zero memory overhead, and the call to the destroyer function involving no indirection.
Basically sweep std::unique_ptr and std::make_unique for factory::unique_ptr and factory::make_unique, then line up the creator/destroyer ADL helpers to make all unique ptrs to a given type just do the right thing.
Test code:
auto ptr = factory::make_unique<some_ns::some_type>( 2, 3 );
std::cout << ptr->x << "=5\n";
std::cout << sizeof(ptr) << "=" << sizeof(std::unique_ptr<some_ns::some_type>) << "\n";
live example.
This has zero runtime overhead over writing custom unique ptr types. If you don't overload creator or destroyer for a tag_t<X> in X's namespace (or in namespace factory), factory::make_unique returns a bog standard std::unique_ptr<X>. If you do, it injects compile-time information how to destroy it.
By default factory::make_unique<X> uses {} initialization so it works with aggregates.
The tag_t system is to enable ADL-based customization of factory::make_unique and factory::unique_ptr. It is useful elsewhere too. End users don't have to know about it, they just need to know you use factory::unique_ptr and factory::make_unique always.
Searching for std::make_unique and std::unique_ptr should find you cases where someone is violating this rule. Eventually (you hope) they'll notice all unique pointers are factory::unique_ptr instead of std.
The magic to add a type to the system is short enough and easy enough to copy that people can do it without having to understand ADL. I'd include a paragraph marked "you don't need to know this, but this is how this works" in comments somewhere.
This is possibly overdone; I was just thinking how I'd handle distributed traits and destruction/creation in c++17 with some of the new features. I haven't tried this in production, so there may be unrevealed problems.
One thing you could do is use a using statement to introduce an alias for std::unique_ptr<T, void (*)(T*)> like
template<typename T>
using uptr = std::unique_ptr<T, void (*)(T*)>;
Then you could use it like
auto u = uptr<int>{create(), &destroy};
You are overcomplicating things. Simply specialize std::default_delete for your custom types, and you can use a vanilla std::unique_ptr.
A shame that std::shared_ptr doesn't use the same customization-point, there you will have to supply the deleter explicitly.
Compiling on C++03, I've attempted to write up a test template function that returns a pointer-to-member-function of a member function that returns int, and takes two float arguments:
template<typename TemplateClass>
int (TemplateClass::*)(float,float) Testtest(TemplateClass &A)
{
return &TemplateClass::Function;
}
But naturally, no matter what variations on the pointer-to-member-function syntax I use, the compiler complains of initialisation errors. Typedef, although it works with known classes, for obvious reasons (naming conflicts), won't accept template class arguments for classes that I can't know ahead of time which are likely to use the same function.
What non-typedef way is there to get this function to compile and return a pointer-to-member-function?
To declare it without a type alias, without type deduction, and without a trailing return type:
template<typename TemplateClass>
int (TemplateClass::* Testtest(TemplateClass &A))(float,float)
But of course this isn't what you would use in real code. Instead you would use an alias template:
template<typename T>
using return_type = int (T::*)(float,float);
template<typename TemplateClass>
return_type<TemplateClass> Testtest(TemplateClass &A)
Or return type deduction in C++14:
template<typename TemplateClass>
auto Testtest(TemplateClass &A)
Or a trailing return type (in C++11):
template<typename TemplateClass>
auto Testtest(TemplateClass &A) -> int (TemplateClass::*)(float,float)
You need this prototype:
template<typename TemplateClass>
int (TemplateClass::*Testtest(TemplateClass &A)) (float,float) { }
int (Class::*f())(float,float);
f is function taking no arguments returning pointer to member function of class Class takinf 2 floats and returning int.
And template version:
template <typename Type>
int (Type::*f())(float,float);
I reject the premise of your question. Use typedefs. Or, specifically, a type trait:
template <class T, class F>
struct make_mem_fun {
typedef F T::*type;
};
template<typename TemplateClass>
typename make_mem_fun<TemplateClass, int(float, float)>::type
Testtest(TemplateClass &A)
{
return &TemplateClass::Function;
}
That is way easier to understand than the convoluted syntax of actually returning the type. With C++11, we can turn that into an alias to drop the typename ::type stuff.
I have a generic question about template functions versus auto type deduction for functions.
For years, we have have been able to write template function :
template <class T> T add(T a,T b){
return a+b;
}
There is a TS for using auto for function's parameters deduction
auto add(auto a,auto b){
return a+b;
}
I though with auto, one had no way to get to the actual type and for instance use static members, but this works just fine :
#include <iostream>
struct foo
{
static void bar(){std::cout<<"bar"<<std::endl;}
static int i ;
};
int foo::i{0};
void t(auto f){
decltype(f)::bar();
std::cout<< decltype(f)::i<<std::endl;
}
int main(int argc, char *argv[])
{
t(foo());
return 0;
}
So is there any reason to choose one instead of the other ?
The obvious reason for this particular code would be that they don't really have the same semantics at all.
In particular, if you pass arguments of different types, the version using auto will deduce a type independently for each, then based on those deduce a type for the result.
Conversely, with the template version, you've specified exactly one type, so the arguments must both be the same type (and the result will be the same).
So, to get the code to be more nearly equivalent, you'd really need to write the template more like:
template <class T, class U>
auto add(T a, U b) -> decltype(a+b) {
return a+b;
}
This is obviously possible as well, but adds even more strength to the arguments in favor of using auto instead.
In your code there are two different uses of auto, one in the parameters and another in the return type. In the case of the parameters, each use of auto introduces a unique template type argument, so as Jerry mentions it would be equivalent to:
// 1
template <typename A, typename B>
auto add(A a, B b) {
return a + b;
}
In this case, if you have any constraint on the different type arguments (must be the same could be one, but there would be other) then the explicit template syntax provides a better alternative. This is specially so if you want to use SFINAE on the arguments (by means of an additional template argument):
// 2
template <typename A, typename B,
typename _1 = typename A::iterator, // A has nested iterator type
typename _2 = typename B::iterator> // So does B
auto f(A a, B b);
Note that I have intentionally avoided the use of SFINAE on the return type as it somehow interferes with your other use of auto, but this could be another option:
// 3
auto f(auto a, auto b)
-> typename enable_if<has_nested_iterator<decltype(a)>::value
&& has_nested_iterator<decltype(b)>::value,
[return type] >::type;
But as you can see it becomes a bit more involved as you need to use a trailing return type and obtain the type trough the value by means of decltype.
The second use of auto in your example, which is quite different than this one, is in the return type to have a deduced return type. The deduced return type is a feature that was already available in C++11 for lambdas, but has been generalized to all function templates. The feature lets the compiler find the type returned by the function by inspecting the different return statements inside the body. The advantage is that if your template has a single return expression, you can avoid having to type that expression twice, one for the return type another for the actual code:
// 4
auto add(auto a, auto b) -> decltype(a + b) { // 'a + b' here
return a + b; // 'a + b' also here
}
The disadvantage is that the compiler needs to inspect the body of the function to determine the type that will be returned, and this is by necessity post type substitution. As such, the return statement from a function having a deduced type cannot be used in an SFINAE expression, potentially complicating the life of users of your function:
// 5
auto doAdd(auto a, auto b)
-> typename enable_if<is_integral<decltype(add(a,b))>>::type
{
return add(a,b);
}
SFINAE will not remove the above doAdd overload from the overload resolution set causing a hard error if you call it as doAdd(1, 1.).
After thinking, the only reason I see for using template function is to enforce several parameters to have the same type. While with auto, each type deduction is independent from each other.
You can even mix template and auto if you want the first and third parameter to have the same type and generic type for the second.
template <class T> void barb(T a,auto b,T c){}
int main(int argc, char *argv[])
{
barb(5," ",5); //ok
barb(5," ",5.6); //fails
return 0;
}
As Oktalist wrote in the comment, you can use a using statement to get the parameter's type auto.
I have the following template class :
template <typename T>
struct timer
{
T period;
timer(T p) :
period(p)
{}
};
To instantiate it I need to do :
timer<double> t(double(0.0));
Is is possible to improve timer's class definition to allow this syntax :
timer t(double(0.0));
and have the compiler infer the double type from the constructor's argument ?
No, you can't do that. Type inference doesn't occur in those situations. You could use the auto keyword and a function template to make things easier though:
template<typename T>
timer<T> make_timer(T value) {
return value;
}
// let the compiler deduce double
auto t = make_timer(0.0);
Note that this use of the auto keyword is only valid in the C++11 standard.
Moreover, for this specific situation, you could typedef a double timer:
typedef timer<double> timer_d;
timer_d t(0.0);
Though I'd still go with the first solution, if you're able to use C++11.
No, it's not possible, deduction only works in functions. The usual solution is to write a make_ function which returns a new instance. This is C++11:
template <typename T>
timer<T> make_timer(T&& p) {
return timer<T>(std::forward<T>(p));
}
auto t = make_timer(0.0);
I currently have a function template, taking a reference, that does something in essence equivalent to:
template <typename T>
void f(T& t)
{
t = T();
}
Now, I can call:
int a;
f(a);
To initialize my variable a.
I can even do:
std::vector<int> a(10);
f(a[5]);
However, this will fail:
std::vector<bool> a(10);
f(a[5]);
The reason being a[5] returns an object with reference semantic, but not a reference. So I need to be able to write:
template <typename T>
void f(T a)
{
a = T();
}
But if I add this new template and try to compile the first example (with int), I obtain the following error:
test_multi_tmpl.cc: In function ‘int main()’:
test_multi_tmpl.cc:20: error: call of overloaded ‘f(int&)’ is ambiguous
test_multi_tmpl.cc:6: note: candidates are: void f(T&) [with T = int]
test_multi_tmpl.cc:12: note: void f(T) [with T = int]
Any ideas how to solve this? I wouldn't like to overload f just for std::vector<bool>::reference as this construct might appears in other places ...
I think specialising f for std::vector<bool>::reference is your only option.
Note that using std::vector<bool> is probably a bad idea in the first place (the std::vector<bool> specialisation is deprecated for future versions of the c++ language) so you could just use std::deque<bool> instead.
I'm not sure whether you already know about this...
The specialization std::vector is not really a STL container because it does not meet the necessary requirements. In particular, it's not possible to created proxied containers that satisfy the STL concepts because of the reference semantics (you can't fake a reference). Check this article for more information. (Also, as Autopulated mentioned there should be a compiler directive to provide control over std::vector in the future C++ Standard.)
A simple workaround that could solve your problem is by overloading function f for this particular type (and others if they appear and are not many). Notice that this is an overload and not an specialization. You might also wanna check this for why not specialize function templates.
void f(std::vector<bool>::reference t)
{
/* ... */
}
There are two ways to do this, one is, as you suggest, specialize for std::vector<bool>::reference. The other is by using type-traits dispatching.
template <class T>
void f (T& t) {
f_impl(t, is_reference_wrapper_type<T>());
}
template <class T>
void f_impl(T& t, mpi::false_) {
t = T();
}
template <class T>
void f_impl(T& t, mpi::true_) {
// do nothing, or voodoo here
}
Note the code above is un-tested and there would be more complex means of dispatching based on a trait -- or a set of traits -- in this situation.
This would also mean that you would need to implement is_reference_wrapper_type like so:
template <class T>
struct is_reference_wrapper_type : mpi::false_ {};
template <>
struct is_reference_wrapper_type<std::vector<bool>::reference> : mpi::true_ {};
Using traits or template specialization would make it work.