Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write a perfect Abbreviated Function Template?

What are the best practices to write abbreviated function templates?

I understand, that since C++20, the following is valid code:

void f(auto&& val) {...}

Is it essential to use decltype(auto) as the return type of such functions? In particular, is the following code the right way to write abbreviated function templates?

decltype(auto) f(auto&& val) {return std::forward<decltype(val)>(val+=2);}
  • How should I specialize the above function?
//something like this?: 
template<> decltype(auto) f<MyClass>(MyClass& val) {...}
like image 330
Adelhart Avatar asked Oct 24 '25 05:10

Adelhart


2 Answers

Is it essential to use decltype(auto) as the return type of such functions?

Depends on what you're returning. Using an abbreviated template doesn't change anything in this regard.

In particular, is the following code the right way to write abbreviated function templates?

decltype(auto) f(auto&& val) {return std::forward<decltype(val)>(val+=2);}

decltype(auto) is used correctly. But val should be forwarded before doing anything with it:

std::forward<decltype(val)>(val) += 2;

Alternatively, you could write:

decltype(val)(val) += 2;

How should I specialize the above function?

//something like this?: 
template<> decltype(auto) f<MyClass>(MyClass& val) {...}

The parameter has type T &&, where T is the implicit template parameter. So it must be either f<MyClass>(MyClass&& val) or f<MyClass &>(MyClass& val). You could also omit the template parameter, and let the compiler infer it.

But note that this specialization applies only to a non-const, rvalue (or lvalue) MyClass argument. If you want to specialize for all value categories and combinations of cv-qualifers of the argument, you'll need to overload it instead (because you can't partially specialize functions):

template <typename T>
requires std::same_as<int, std::remove_cvref_t<T>>
decltype(auto) f(T&& val) {return 42;}

Or:

decltype(auto) f(auto&& val)
requires std::same_as<int, std::remove_cvref_t<decltype(val)>>
{return 42;}
like image 52
HolyBlackCat Avatar answered Oct 26 '25 19:10

HolyBlackCat


Is it essential to use decltype(auto) as the return type of such functions?

No.

From [dcl.fct]/18/sentence-2 [extract, emphasis mine]:

An abbreviated function template is equivalent to a function template ([temp.fct]) whose template-parameter-list includes one invented type template-parameter for each generic parameter type placeholder of the function declaration, in order of appearance. For a placeholder-type-specifier of the form auto, the invented parameter is an unconstrained type-parameter.

Thus, there is no essential difference between regular function templates and abbreviated function templates w.r.t. the usage of decltype(auto) as return type; it simply gives you the ability to perfectly forward a return type (as with a regular function template).


How should I specialize the above function?

As per [dcl.fct]/18 [extract, emphasis mine]:

[...] [ Example:

template<typename T>     concept C1 = /* ... */;
template<typename T>     concept C2 = /* ... */;
template<typename... Ts> concept C3 = /* ... */;

void g1(const C1 auto*, C2 auto&);
void g2(C1 auto&...);
void g3(C3 auto...);
void g4(C3 auto);

These declarations are functionally equivalent (but not equivalent) to the following declarations.

template<C1 T, C2 U> void g1(const T*, U&);
template<C1... Ts>   void g2(Ts&...);
template<C3... Ts>   void g3(Ts...);
template<C3 T>       void g4(T);

Abbreviated function templates can be specialized like all function templates.

template<> void g1<int>(const int*, const double&); // OK, specialization of g1<int, const double>

— end example ]

you can specialize, say

void f(auto&& val) { (void)val;}

as

template<>
void f<int>(int&& val) { (void)val;}

DEMO.

like image 27
dfrib Avatar answered Oct 26 '25 19:10

dfrib



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!