Why that partial template specialization is allowed? - c++

I've looked at a partial template specialization argument-list and stumbled upon such an example
template <typename>
class function;
template <typename ReturnValue, typename Args,typename Args2>
class function<ReturnValue(Args,Args2)>
{
//smth
};
What exactly means ReturnValue(Args,Args2) (couse it's not a type as I know) and by what rules can I write similar things?
Thanks

couse it's not a type as I know
Actually, it is. ReturnValue(Args,Args2) is the type "function taking Args and Arg2 and returning ReturnValue". Take this example:
void foo(int, char);
typedef void FunctionType(int, char);
Here, FunctionType is indeed the type void(int, char), which is also the type of foo.

Related

Determining the Parameter Types of an Undefined Function

I've recently learned that I cannot:
Take the address of an undefined function
Take the address of a templatized function with a type it would fail to compile for
But I've also recently learned that I can call decltype to get the return type of said function
So an undefined function:
int foo(char, short);
I'd like to know if there's a way that I can match the parameter types to the types in a tuple. This is obviously a meta programming question. What I'm really shooting for is something like decltypeargs in this example:
enable_if_t<is_same_v<tuple<char, short>, decltypeargs<foo>>, int> bar;
Can anyone help me understand how decltypeargs could be crafted?
For non-overloaded functions, pointers to functions, and pointers to member functions, simply doing decltype(function) gives you the type of the function in an unevaluated context, and that type contains all the arguments.
So to get the the argument types as a tuple, all you need are a lot of specializations:
// primary for function objects
template <class T>
struct function_args
: function_args<decltype(&T::operator()>
{ };
// normal function
template <class R, class... Args>
struct function_args<R(Args...)> {
using type = std::tuple<Args...>;
};
// pointer to non-cv-qualified, non-ref-qualified, non-variadic member function
template <class R, class C, class... Args>
struct function_args<R (C::*)(Args...)>
: function_args<R(Args...)>
{ };
// + a few dozen more in C++14
// + a few dozen more on top of that with noexcept being part of the type system in C++17
With that:
template <class T>
using decltypeargs = typename function_args<T>::type;
This requires you to write decltypeargs<decltype(foo)>.
With C++17, we will have template <auto>, so the above can be:
template <auto F>
using decltypeargs = typename function_args<decltype(F)>::type;
and you'd get the decltypeargs<foo> syntax.

Unnamed class/typename in template arguments

I was looking through the documentation of SFINAE and there was this template declaration:
template<typename SomeType>
struct inner_type { typedef typename SomeType::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename inner_type<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo(int) {}
Specifically, I'm asking about class = typename T::type. What's the point of declaring an unnamed class?
Because of the comment I thought that this will result in a compiler error when T doesn't have a member type, but that isn't the case, as foo<int, int, int>(0); compiles fine.
On the other hand
template<class T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
void foo(T t) {}
doesn't compile if T is signed, and compiles if T is unsigned.
What am I missing here?
foo<int, int, int>(0); compiles fine.
Because you specify the 2nd template argument, then the default template argument (i.e. typename T::type) won't be used, then won't trigger compile error.
If you just write foo<int>(0); to make the default template argument to be used, compile will fail.
LIVE
And it's same for your 2nd sample too.
What's the point of declaring an unnamed class?
Because the template parameter won't be used for the template implementation.

Parameter pack must be at the end of the parameter list… When and why?

I don't get the reason for which a parameter pack must be at the end of the parameter list if the latter is bound to a class, while the constraint is relaxed if the parameter list is part of a member method declaration.
In other terms, this one compiles:
class C {
template<typename T, typename... Args, typename S>
void fn() { }
};
The following one does not:
template<typename T, typename... Args, typename S>
class C { };
Why is the first case considered right and the second one is not?
I mean, if it's legal syntax, shouldn't it be in both the cases?
To be clear, the real problem is that I was defining a class similar to the following one:
template<typename T, typename... Args, typename Allocator>
class C { };
Having the allocator type as the last type would be appreciated, but I can work around it somehow (anyway, if you have a suggestion it's appreciated, maybe yours are far more elegant than mine!!).
That said, I got the error:
parameter pack 'Args' must be at the end of the template parameter list
So, I was just curious to fully understand why it's accepted in some cases, but it is not in some others.
Here is a similar question, but it simply explains how to solve the problem and that was quite clear to me.
It is valid for function templates but only when argument deduction can help the compiler resolve the template parameters, as it stands your function template example is virtually useless because
template<typename T, typename... Args, typename S> void fn() { }
int main() { fn<int, int, int>(); }
test.cpp: In function 'int main()':
test.cpp:2:32: error: no matching function for call to 'fn()'
int main() { fn<int, int, int>(); }
^
test.cpp:1:57: note: candidate: template<class T, class ... Args, class S> void fn()
template<typename T, typename... Args, typename S> void fn() { }
^
test.cpp:1:57: note: template argument deduction/substitution failed:
test.cpp:2:32: note: couldn't deduce template parameter 'S'
int main() { fn<int, int, int>(); }
the compiler has no way of determining which template parameters belong to the parameter pack, and which to S. In fact as #T.C. points out it should actually be a syntax error because a function template defined in this manner cannot ever be instantiated.
A more useful function template would be something like
template<typename T, typename... Args, typename S> void fn(S s) { }
as now the compiler is able to unambiguously match the function parameter s with the template type S, with the side effect that S will always be deduced - all explicit template parameters after the first will belong to Args.
None of this works for (primary) class templates, parameters aren't deduced and it's expressly forbidden:
From draft n4567
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4567.pdf
[temp.param] / 11
[...]If a template-parameter of a primary class template or alias
template is a template parameter pack, it shall be the last
template-parameter.[...]
(if they were deduced it would be ambiguous as in the function template example).
The first one is not right. The compiler is just buggy and failed to diagnose it. [temp.param]/11:
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 (14.8.2).
If the function type T(Args...) is meaningful to the end-user, one way to fix this would be to use a partial specialization instead:
template<class F, class Alloc> class C; //undefined
template<class T, class... Args, class Alloc>
class C<T(Args...), Alloc> {
// implementation
};
Depending on the actual requirements, type-erasing the allocator might also be worth considering.

What does this syntax mean, `class template <class R, class …Args> class name<R(Args…)>`

I've been trying more about multi threaded programming in c++, and i was having difficulty understanding std::promise so i began searching for answers on this website, and low and behold, there is somebody with the same question as me. But reading the answer made me even more confused
this is the code in the answer that presumably is a similar implementation of std::packaged_task
template <typename> class my_task;
template <typename R, typename ...Args>
class my_task<R(Args...)>
{
std::function<R(Args...)> fn;
std::promise<R> pr; // the promise of the result
public:
template <typename ...Ts>
explicit my_task(Ts &&... ts) : fn(std::forward<Ts>(ts)...) { }
template <typename ...Ts>
void operator()(Ts &&... ts)
{
pr.set_value(fn(std::forward<Ts>(ts)...)); // fulfill the promise
}
std::future<R> get_future() { return pr.get_future(); }
// disable copy, default move
};
in this code,
1- what does this syntax mean template <typename R, typename ...Args> class my_task<R(Args...)>, more specifically, what is the purpose of <R(Args...)> ?
2- why is there a foroward decleration for the class?
thanks
There was some brief discussion in the comments how 1 and 2 should be two separate questions, but I believe that they both are just two sides to the same exact question, for the following reasons:
template <typename> class my_task;
template <typename R, typename ...Args>
class my_task<R(Args...)>; ....
The first statement declares a template that takes a typename as its sole template parameter. The second statement declares a specialization for that template class.
In this context:
R(Args...)
Will specialize for any typename that matches a function. This template specialization will match any template instantiation that passes a function signature for a typename. Barring any problems within the template itself, this template specialization will be used for:
my_task<int (const char *)>
or, a function that takes a const char * parameter and returns an int. The template specialization will also match:
my_task<Tptr *(Tptr **, int)>
or, a function that takes two parameters, Tptr ** and an int, and returns a Tptr * (here, Tptr is some other class).
The template specialization will NOT match:
my_task<int>
Or
my_task<char *>
Because they are not function signatures. If you try to instantiate this template using a non-function typename you're going to get a compilation error. Why?
Well, that's because the template is not defined:
template<typename> class my_task;
Don't think of this as just a forward declaration. it's a forward declaration of a template that takes a template parameter, and the template will not be defined anywhere. Rather, the template declaration allows for a subsequent template specialization declaration, that will match only specific types passed as a template parameter.
This is a common programming technique for restricting the kinds of typenames or classes that can be used with a particular template. Instead of allowing a template to be used with just any typename or class, the template can only be used with some subset. In this case, a function typename, or signature.
It also makes it easier for the template itself to explicitly reference -- in this case -- to the template parameter's return type, and the parameter types. If the template has just a bland, single typename as a template parameter, it can't easily access the function's return type, or the function parameter's types.
1: What does this syntax mean template <typename R, typename ...Args> class my_task<R(Args...)>
This is a specialization of the class template my_task. The <R(Args...)> after the name means it is specialized for that type, and that type is a function. R(Args...) is the type of a function taking Args parameters and returning R. So, my_task<void()> mt; for example would make Args be an empty parameter pack, and R would be void.
2: Why is there a forward declaration for the class?
The class is declared, but unlike an ordinary forward declaration, the un-specialized version isn't defined. This class is only intended to work when the type is a function, so if someone tries to use something that isn't a function (like my_task<int>), it will give an error about the type being undefined.
my_task<void*(int, int)> mt1; //R = void*, Args = int, int
my_task<int> mt2; //error: use of undefined class

deduce template argument for typedef'd function pointer

Is there a way to deduce a template argument for a function pointer when using a typedef? My sample code is:
struct A {};
void func(const A&) {};
template <typename T>
struct FuncPtr
{
typedef void(*Type)(const T&);
};
void f_(FuncPtr<A>::Type) {}
template <typename T> // I'd like to hide the messy function pointer in a typedef
void f1(typename FuncPtr<T>::Type fp) { f_(fp); }
template <typename T> // this works, but "fp" is really several lines long
void f2(void(*fp)(const T&)) { f_(fp); }
With that in place, I can call f2(AFunc). But I'd rather have something closer to f1(func) because in my actual code the declaration for the function pointer is much longer. And I need to have an actual function pointer, rather than just passing a template argument, so that I can call f_().
Not with your current setup. Everything to the left of ::* is a non-deduced context, because a metafunction can apply arbitrary Turing-complete transformations on the type, and in any event there's no guaranteed one-to-one mapping between the original type and the result. So the language doesn't even try.
But an alias template will work:
template <class T>
using FuncPtr = void (*)(const T&);
template <typename T>
void f3(FuncPtr<T> fp) { return f_(fp); }
*Formally known as the "nested-name-specifier of a type that was specified using a qualified-id" ([temp.deduct.type]/p5).
As “Effective Modern C++” Item 9,Prefer alias declarations to typedefs says:
In particular, alias declarations may be templatized (in which case
they’re called alias templates), while typedefs cannot. This gives
C++11 programmers a straightforward mechanism for expressing things
that in C++98 had to be hacked together with typedefs nested inside
templatized structs.For example, consider defining a synonym for a linked list that uses a custom allocator, MyAlloc. With an alias template, it’s a piece of cake:
template<typename T
using MyAllocList = std::list<T, MyAlloc<T>>;
MyAllocList<Widget> lw;
With a typedef, you pretty much have to create the cake from
scratch:
template<typename T>
struct MyAllocList {
typedef std::list<T, MyAlloc<T>> type;
};
MyAllocList<Widget>::type lw;

Resources