Template variadic argument referenced from another argument - c++

I'm in trouble for the way I should reference arguments in a template (honestly, I strongly suspect that it's not possible to achieve what I'd like to do).
It follows an example of what I'd like to do (of course, this is not syntactically legal, the aim is to give the idea of which is the target):
template<class C, Ret(C::*Member)(Params...), typename Ret, typename... Params>
class MyClass { }
In other terms, I'd like to reference a member of a class, by specifying at the same time also which is the returned value and the parameters of that method.
Unfortunately, the only way I see to do that is something like the following one (well, it depends indeed on where those typenames are required, anyway it may be a meaningful example):
template<typename Ret, typename... Params>
class MyClass {
public:
template<class C, Ret(C::*Member)(Params...)>
MyClass(C *c) { /* do something else and give sense to this class */ }
}
Besides the one above, that is to break the interlacing by introducing a templated constructor, there exists another valid approach to obtain the same result with the sole class template signature?
I know (pretty simple) how to achieve that in case of not variadic template (as an example, move Ret before Member), but the variadic one (Params) has to lay at the end of the template list and I cannot refer it in any way.

According with this question, a viable solution could be to rely on deduction forced by a default value.
As an example, the following code should be valid:
class MyClass {
public:
template <class C, typename R, typename... P, R(C::*M)(P...) = &C::foo>
void bar(C *c) { }
};
I cite part of the linked question (a citation that is a citation for itself, I'm in a loop):
A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument.
Because of that, the following code should not be allowed instead, even if it compiles with GCC:
class MyClass {
public:
template <class C, typename R, typename... P, R(C::*M)(P...)>
void bar(C *c) { }
};
Well, quite tricky, not so flexible a solution and honestly I ended up far ago with a bit of refactoring, but for the sake of clarity I've decided to add an answer and close the question with a snippet that compiles.

Related

Why can the type constraint `std::convertible_to` be used with only one template argument?

I've scrolled and searched through the standard and cppreference for hours to no avail, would really appreciate if someone could explain this occurance for me:
I am looking at the standard concept std::convertibe_to. Here's a simple example that I do understand
class A {};
class B : public A {};
std::convertible_to<A, B>; // false
std::convertible_to<B, A>; // true
Works as expected.
Now there is also another possible way to use it, that I don't quite understand
void foo(std::convertible_to<A> auto x) { /* ... */ }
, and this function can easily accept any type convertible to A. This is weird though, because the first template parameter ("From") is essencially dropped, and deduced on function call. This following function would also work, and I'm fairly certain it's actually equivalent to the previous one
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
again the type of x is deduced when we call foo.
This works, despite the template requiring two parameters. I tried also with std::derived_from and it seems to work. This form of specifying a concept with only one template parameter even appears in the standard itself, so there must be some piece of syntax that explains it.
Notice that the only version of std::convertible_to that exists is in fact one that takes two template parameters.
Could anyone clarify why this works?
void foo( constraint<P0, P1, P2> auto x );
this translates roughly to
template<contraint<P0, P1, P2> X>
void foo( X x );
which translates roughly to
template<class X> requires constraint<X, P0, P1, P2>
void foo( X x );
notice how the type X is prepended to the template arguments of the constraint.
So in your case,
template<typename T, std::convertible_to<T> S>
void foo(S x) { /* ... */ }
is roughly
template<typename T, class S>
requires std::convertible_to<S, T>
void foo(S x) { /* ... */ }
(I say roughly, because I believe they are not exactly equivalent in subtle ways. For example, the second one introduces the name X, while the first does not. And there are probably other differences of similar scale; what I mean is that understanding the translation will give you an understanding of what is translated. This is unlike for(:) loop-for(;;) loop correspondence; the standard specifies for(:) loops in terms of for(;;) loops, which isn't what I'm claiming above.)
There are several locations where a concept name can be used where the first argument to the template concept is not supplied in the template argument list. Constraining an auto deduced variable is one of them.
The first argument in these cases is provided by some expression, typically using template argument deduction rules. In the case of a constrained function parameter, the first argument is determined by the template function itself. That is, if you call foo(10), template argument deduction will deduce the auto template parameter as an int. Therefore, the full concept will be convertible_to<int, A>.

Derive earlier template parameters when later parameter given

Given
template <typename S, typename T>
T make_T(S const &s) { ... }
How can I leave S to be derived while explicitly providing T?
I would like to be able to say:
auto t = make_T<auto, int>(S{});
but clang and gcc tell me that auto is not allowed in template argument.
Had the arguments happened to be reversed in the prototype of make_T,
then all would be well;
I could explicitly give T and leave S to be derived.
In a previous question,
the proposed solution was to declare a helper function that reversed the arguments, e.g.,
template <typename T, typename S>
T make_T_reversed(S const &s) { return make_T<S,T>(s); }
which now enables
auto t = make_T_reversed<int>(S{});
as desired, but I'm hoping there might be a more direct way that doesn't require creating temporary helper functions. I'm asking as a new question because the accepted answer of the previous question doesn't answer my actual question:
is there a direct means of achieving this?
I'm feeling hopeful that with C++17 and C++20 (not around at the time of the previous question), there may now be, but I've sadly been unable to find it.
Further motivating examples
The use case initially motivating the question was that I wanted to write
std::unordered_set<T, default, default, Allocator> obj;
using the default values for the middle two template parameters
(Hash and KeyEqual),
but explicitly specifying the Allocator parameter.
I'm using the default constructor, so the type for Allocator cannot be derived.
I realise the question I actually asked isn't quite the same (I asked about deriving the values rather than taking the default values), but I'm hoping the same approach would work for both cases:
auto t = make_T<auto, int>(S{});
std::unordered_set<T, auto, auto, Allocator> obj;
if S is from T when T is provided, what you will provide for S?
make_T<auto, int>() is definitely impossible, but make_T<void, int>() may be acceptable for you?
template<typename nS, typename T, typename S = std::conditional_t<std::is_same_v<void, nS>, T, nS>>
T make_T(S const&);
but S is always deduced by the argument, why do you want S to be the first parameter?
or you want S to be determined?
template<typename nS, typename T>
T make_T(std::conditional_t<std::is_same_v<void, nS>, T, nS> const&);

Understanding this highly templated C++ function binder

template<typename Container, typename Ret, typename ...Args>
struct BindImpl {
template<Ret (Container::*MemberFunc)(Args...)>
class Callable {
public:
inline constexpr Callable (Container *container) :
m_container(container)
{}
inline Ret operator() (Args ...args) const
{
return (m_container->*MemberFunc)(std::forward<Args>(args)...);
}
inline Function<Ret(Args...)> toFunction() const
{
return Function<Ret(Args...)>(*this);
}
private:
Container *m_container;
};
};
template<typename Container, typename Ret, typename ...Args>
BindImpl<Container, Ret, Args...> DeduceImpl (Ret (Container::*)(Args...));
This code is called like this:
(typename decltype(::AIpStack::BindPrivate::DeduceImpl(&EthIpIface::driverSendIp4Packet)) ::template Callable<&EthIpIface::driverSendIp4Packet>((this)).toFunction())
I'm trying to understand what this code does. It apprently is a way to bind function pointers (like &EthIpIface::driverSendIp4Packet) to something.
The line above is from this macro, which fills this struct member, if anyone is intersted. You may wanna have a loot at Function.
The first part that I don't understand is
template<Ret (Container::*MemberFunc)(Args...)>
For me a template must be followed by typename. Also, what follows typename, is the thing to be substituted for. I don't see how this template makes Callable templated. I don't know where something goes to in Callable<something>.
Also, what is DeduceImpl? Looks like a function declaration but without a definition.
Also, what Container::*MemberFunc means?
Firstly, templates can also take in non-type parameters as well as with typename and class. In this case:
template<Ret (Container::*MemberFunc)(Args...)>
This is a template taking a function pointer as a parameter, where Ret is the return type, Container::*MemberFunc is the pointer to a specific member function in Container with Args... referencing variadic arguments. This gives the pointer the identifier MemberFunc. I have a feeling the asterisk following the scope resolution operator confused you, as usually you would receive a compiler error if you used these two together in any other situation but in this specific case these two are considered one token ::* representing this kind of template parameter instead of the two :: and *.
For this line:
BindImpl<Container, Ret, Args...> DeduceImpl (Ret (Container::*)(Args...));
It is a function declaration. This is a function named DeduceImpl that will return a BindImpl struct that takes a function pointer as an argument. I'm inferring that this function is the interface by which you bind the function pointer, hence the (probably) shortened names "Deduce Implementation" and "Bind Implementation" From what I've read, this function is only used for decltype, so there's no actual definition for this function.
For how this template is actually being utilized in this line (reformatted for easier reading):
typename decltype(::AIpStack::BindPrivate::DeduceImpl(&EthIpIface::driverSendIp4Packet))
::
template Callable<&EthIpIface::driverSendIp4Packet>(this).toFunction()
This is a template disambiguator created just so the compiler knows that the actual template is being utilized instead of a less-than comparison operator.
You wouldn't write all of this just to use the template. This line was probably written because it's one of the few ways the template is instantiated in the project.
In summary:
template<Ret (Container::*MemberFunc)(Args...)> is a template that takes a function pointer referred to as MemberFunc as a parameter.
DeduceImpl returns a BindImpl struct by taking in the function pointer you want to bind.

Variadic templates, parameter pack and its discussed ambiguity in a parameter list

In this question, I'll refer to my previous question.
In that question, I found that the following is not valid:
template<typename T, typename... A, typename S>
class C { };
This is because:
[It is not valid code] for class templates because their arguments must always be specified, which will always result in an ambiguity unless the parameter pack is at the end and slurps up any remaining template parameters.
That makes sense, of course and I got it.
Then, as an alternative approach, the following that involves a specialization has been proposed:
template<typename F, typename S>
class C;
template<typename T, typename... A, typename S>
class C<T(A...), S> { };
Actually, it seems to work, so thanks to the one that proposed it.
Anyway, what I don't understand is why this is valid code while the previous one was not.
Should it suffer from the same ambiguity of the previous solution? Why and how does the compiler solve that ambiguity in that case?
According with the previous question (see the link at the start of this question), it seems to me that still the variadic part should slurp up any parameters to the end, thus this code should not be valid as well.
I'm wrong, of course, but what's wrong exactly in my reasoning?
In the class template, a prospective template argument list C<a,b,c,d,e,f> needs to match
template<typename T, typename... A, typename S>
in which ...A is just floating in a parameter list.
But in the specialization, what needs to be matched is not the list of template variabled but rather the pattern:
C<T(A...), S>
which is easy because the A... is delimited.
So in the template specialization, the list of parameters is just an inventory of symbols, some scalar and some parameter packs, which will appear in a pattern.

Understanding C++ template metaprogramming

In my quest to better understand templates and meta-programming in C++ I'm reading this article, but my understanding of the code snippets quickly diminishes, e.g.:
template<class A, template<class...> class B> struct mp_rename_impl;
template<template<class...> class A, class... T, template<class...> class B>
struct mp_rename_impl<A<T...>, B>
{
using type = B<T...>;
};
template<class A, template<class...> class B>
using mp_rename = typename mp_rename_impl<A, B>::type;
The code is used like:
mp_rename<std::pair<int, float>, std::tuple> // -> std::tuple<int, float>
mp_rename<mp_list<int, float>, std::pair> // -> std::pair<int, float>
mp_rename<std::shared_ptr<int>, std::unique_ptr> // -> std::unique_ptr<int>
Can someone please explain the code like I'm five?
I do have a general and basic understanding of non-templated C++.
What I don't get is:
Why is mp_rename_impl forward declared with two type parameters (class A, template<class...> class B), then it's defined and specialized at the same time[*] with three (template<class...> class A, class... T, template<class...> class B) and respectively two(A<T...>, B) type parameters?
I understand that it aliases (using type = B<T...>;) the type to be B<T...> instead of A<T...>, but I don't really get how it is done.
Also why is A a template template parameter only in the specialization?
[*] most certainly I got something wrong here
Why is mp_rename_impl forward declared with two type parameters (class A, template<class...> class B), then it's defined and specialized at the same time[*] with three (template<class...> class A, class... T, template<class...> class B) and respectively two(A<T...>, B) type parameters?
The forward declaration establishes the number of parameters needed to instantiate mp_rename_impl, and that the former should be an actual type, the latter a template.
Then when there's an actual instantiation, it tries to match the specialisation struct mp_rename_impl<A<T...>, B>, and in doing so it can consider any combination of values for the specialisation's A, T... and B matching the specialisation's expectations: namely template<class...> class A, class... T, template<class...> class B. Note that the A parameter in the specialisation shares a name with the A in the declaration, but is not the same - the former being a template and the latter a type. Effectively, to match the specialisation a template instantiation must have been passed as the declaration's A parameter, and that template's parameters are captured at T.... It imposes no new restrictions on what can be passed as B (though the using statement does - B<T...> needs to be valid or you'll get a compilation error - too late for SFINAE to kick in).
Also why is A a template template parameter only in the specialization?
The specialisation calls that parameter A, but it's not conceptually the same as A in the declaration. Rather, the former's A<T...> corresponds to the latter A. Perhaps the specialisation should have called it "TA" or something else to indicate it's the template from which an actual A can be formed in combination with the T... parameters.
The specialisation is then of A<T...>, B, so the compiler works backwards from whatever instantiation is actually attempted to find valid substitutions for A, T... and B, guided by the restrictions on their form specified in template<template<class...> class A, class... T, template<class...> class B>.
What this achieves it to make sure the specialisation is only matched when the two parameters are a template already given some set of parameters types, and a template able to take a list of parameter types. The matching process effectively isolates the T type list so it can be reused with B.
My first attempt wasn’t what you were looking for, so let me briefly try to go back and explain like you’re six.
It’s not forward-declared in the sense that a function has a prototype and a definition. There’s an implementation for any A, and that compiles to an empty struct (which is a unique type to the compiler, but doesn’t require any actual storage or run-time code). Then, there’s a second implementation, only for template classes A.
There are really two templates in the second definition. What’s going on is that the second definition it takes the two parameters A and ... T and turns them into a type A<T>, which becomes the first parameter of mp_rename_impl<A<T...>,B>. So it applies to any A that’s a template class. But that’s a more specific kind of A! So it’s a specialization that needs to declare a struct with a type definition in its scope. Finally, the third variant is not a specialization of the template at all. It’s declaring the templated mp_rename as an alias for the more complicated type stored within the scope of every struct in the second declaration, which as you see is the identifier type in the scope mp_rename_impl<A, B>. Believe it or not, this makes his template code a lot more readable.
Why does the more generic definition at the top expand to just an empty struct? When A is not a template class, the contents are trivial, but it does need some kind of type to its name so the compiler will consider it distinct from every other type. (It would have been more cool to write my example below to generate classes with the static constants as members, rather than functions. In fact, I just did.)
Updated as threatened to make my templates a bit more like his:
Okay, template metaprogramming is a kind of programming where, instead of having the program compute something when it runs, the compiler computes it ahead of time and stores the answer in the program. It does this by compiling templates. This can be a lot faster to run, sometimes! But there are limits on what you can do. Mainly, you can’t modify any of your parameters, and you’ve got to be sure the computation stops.
If you’re thinking, “You mean, just like functional programming?” you are one very smart five-year-old. What you normally end up doing is writing recursive templates with base cases that expand either to unrolled, streamlined code, or to constants. Here’s an example that might seem familiar from your Intro to Computer Science class when you were three or maybe four:
#include <iostream>
using std::cout;
using std::endl;
/* The recursive template to compute the ith fibonacci number.
*/
template < class T, unsigned i >
struct tmp_fibo {
static const T fibo = tmp_fibo<T,i-1>::fibo + tmp_fibo<T,i-2>::fibo;
};
/* The base cases for i = 1 and i = 0. Partial struct specialization
* is allowed.
*/
template < class T >
struct tmp_fibo<T,1U> {
static const T fibo = (T)1;
};
template < class T >
struct tmp_fibo<T,0U> {
static const T fibo = (T)0;
};
int main(void) {
cout << "fibo(50) = " << tmp_fibo<unsigned long long, 50>::fibo
<< ". fibo(10) = " << tmp_fibo<int, 10>::fibo << "."
<< endl;
return 0;
}
Compile to assembly language, and we see what code the compiler generated for the line tmp_fibo<unsigned long long, 50>::fibo Here it is, in full:
movabsq $12586269025, %rsi
The templates generates an integral constant within each structure at compile-time. What those examples are doing, since you can declare type names within structs, is doing the same thing for types.
I will try to make it simple. Template metaprogramming is about computing types at compile-time (you can also compute values, but just let us focus on that).
So if you have this function:
int f(int a, int b);
You have a function that returns an int value given two int values.
You use it like this:
int val = f(5, 8);
Metafunctions operate on types, instead of on values. A metafunction looks like this:
//The template parameters of the metafunction are the
//equivalent of the parameters of the function
template <class T, class U>
struct meta_f {
typedef /*something here*/ type;
};
Namely, a metafunction has a nested type inside, by convention the nested type is called type.
So you invoke a metafunction like this in non-generic contexts:
using my_new_type = meta_f<int, float>::type;
In generic contexts you must use typename:
using my_new_type = typename meta_f<T, U>::type;
This returns a type, not a run-time value, since we said metafunctions operate on types.
Examples of metafunctions in the standard library can be found in header
type_traits, among others. You have add_pointer<T> or decay<T>. These metafunctions return new types given a type.
In C++14, in order to avoid pieces of code like this, which are verbose:
using my_computed_type = typename std::add_pointer<T>::type;
Some template aliases with a _t suffix, again by convention, were created that invoke the metafunction, directly, for you:
template <class T>
using add_pointer_t = typename std::add_pointer<T>::type;
And now you can write:
using my_computed_type = std::add_pointer_t<T>;
All in all, in a function, you have runtime values as parameters, in a metafunction, the parameters are types. In a function you invoke with the
usual syntax and obtain a runtime value. In a metafunction you get the ::type nested type and get a new computed type.
//Function invocation, a, b, c are values of type A, B, C
auto ret = f(a, b, c);
//Meta function invocation. A, B, C are types
using ret_t = typename meta_f<A, B, C>::type;
//Typical shortcut, equivalent to metafunction invocation.
using ret_t = meta_f_t<A,B,C>;
So for the first function you get a value, for the others, you get a type, not a value.