Deduction of the function - c++

Let's say we have a class template like this:
template<typename F>
class A
{
public:
template<typename... Args>
A(F f, Args... args)
{ /* Do something... */ }
};
And now I want to use it in some way like this one:
A<int(int)> a(::close, 1);
Now the question: is there any way to omit the <int(int)> because a compiler can know this information for the ::close? There is no need to save the "design" of the template.
As for concrete task, I need to design a template of a class. Objects of this class could take a function and parameters for this function at construction time and call this function later.

No, you (currently) cannot. The standard way of doing this is by creating "make_like" function (such as make_pair, make_optional ...):
template<typename F, typename... Args>
A<std::decay_t<F>> make_A (F &&f, Args&&... args) {
return {std::forward<F>(f), std::forward<Args>(args)...};
}
C++17 will introduce template argument deduction for class which will allow you to do exactly what you want (see also Barry's answer below).

Thanks to the adoption of template parameter deduction for constructors, in C++17, you'll be able to just write:
A a(::close, 1);
Before that, you'll just need to write a factory to do the deduction for you:
template <class F, class... Args>
A<std::decay_t<F>> make_a(F&& f, Args&&... args) {
return {std::forward<F>(f), std::forward<Args>(args)...};
}
auto a = make_a(::close, 1);
This is a little verbose, but at least you don't need to worry about efficiency - there will be no copies made here thanks to RVO.

You cannot omit the arguments of a template class, unless they are defaulted. What you can do is have a maker function which deduces the argument and forwards this argument to the template class, returning an object of the appropriate instantiation.
template<typename F, typename... Args>
A<F> make_A(F f, Args&&... args) {
return A<F>(f, std::forward<Args>(args)...);
}

Related

How to create a wrapper for `std::make_unique<T>`?

I'd like to create a wrapper for std::unique_ptr<T> and std::make_unique<T> because I think they look ugly and take too long to type. (Yes, I'm that kind of person).
I have completed my UniquePtr type alias with no problem but cannot get my MakeUnique to work. It seems to be a bit of a rabbit hole, and was wondering if anyone here might be able to give me a hand with this?
What I have so far:
template <class T>
using UniquePtr = std::unique_ptr<T>;
template<typename T, typename... Args>
UniquePtr<T> MakeUnique<T>(Args... args) // recursive variadic function
{
return std::make_unique<T>(args);
}
Many thanks in advance!
You need to forward properly the values, and you need to expand the pack.
First, make it compile:
template<typename T, typename... Args>
UniquePtr<T> MakeUnique(Args... args) // not recursive
{ // ^---- no need for <T> when defining function template
return std::make_unique<T>(args...); // the ... expands the pack
}
Then, you need to forward, because args... will copy everything. You want to move rvalues, and copy lvalues:
template<typename T, typename... Args>
UniquePtr<T> MakeUnique(Args&&... args)
{
return std::make_unique<T>(std::forward<Args>(args)...);
}

Create object using parameters pack in template

I'd like to create template function that would create object basing on template typename and parameters pack.
I created a function that is supposed to create object based on typename from template, and I would also like to pass parameters pack to that template i order to pass parameters to constructor. Is this correct?:
template<typename TComponent, typename... Args>
void CreateComponent(Args... args)
{
std::shared_ptr<TComponent> component = std::make_shared<TComponent>(args ...);
}
I also wanted to pass those parameters to another fucntion like this:
template<typename TComponent, typename... Args>
void AddComponent(Args... args)
{
m_world->AddComponent<TComponent, Args>(m_id, args...);
}
But compiler returns an error " 'args' parameter pack must be expanded in this context"
Is it even possible to achieve what I want to achieve ?
But compiler returns an error " 'args' parameter pack must be expanded in this context"
Yes: you've forgotten to expand the types
m_world->AddComponent<TComponent, Args...>(m_id, args...);
// ...................................^^^
As pointed by Jarod42, according to the circumstances, you could avoid to explicit the Args... expansion
m_world->AddComponent<TComponent>(m_id, args...);
// no more Args...
and let the compiler deduce the types through args... (but we should see the AddComponent() definition).
Anyway, I don't see errors in your CreateComponents() function but, as correctly says François Andrieux in a comment, you don't using perfect forwarding.
It's a too-great argument to explain in an answer but, this way, you're renouncing to move semantics advantages (that is: you, potentially, make some unnecessary copies).
The following is your CreateComponents() function enabling perfect forwarding
template <typename TComponent, typename ... Args>
void CreateComponent (Args && ... args)
{ // .....................^^ forwarding reference added
std::shared_ptr<TComponent> component
= std::make_shared<TComponent>(std::forward<Args>(args)...);
} // ..............................^^^^^^^^^^^^^^^^^^^^^^^^

How is a template function evaluated by a compiler?

Consider the following variadic template function:
template<typename T, typename... Args>
auto foo(Args... args) -> T[sizeof...(args)]
{
T t[sizeof...(args)];
// Maybe do some pack expansion/assignment
return t;
}
with the instantiation:
// total should be of type int[5];
auto total = foo(1,2,3,4,5);
I understand that this will not compile, due to the return type not being deducible, but I do not understand why it is not deducible.
Is there something about this function that the compiler does/can not know at compile time, or the order of parts of the function being evaluated, which prevents type deduction?
I suspect it is due to the calling of the function sizeof..., which I think evaluates at run-time. If so, is there a compile-time equivalent?
You cannot return built-in arrays by value. Use std::array instead.
Also, as it stands, you'll need to explicitly provide the type T as a template argument as it does not appear in the argument list. Thus the compiler has nothing to deduce it from.
Complete example:
#include <array>
template<typename T, typename... Args>
auto foo(Args... args) -> std::array<T, sizeof...(args)>
{
std::array<T, sizeof...(args)> t;
// Maybe do some pack expansion/assignment
return t;
}
int main () {
auto a = foo<int>(1,2,3);
}
Depending on the usecase, you may get rid of the explicit template argument by e.g. using the std::common_type of all elements in the pack or static_asserting they all have the same type and use that.
Also, for the record, sizeof... does yield a compile-time constant. The problem is, to make the answer explicit, that the compiler has no way tell what T is supposed to be.
Compiler unable to deduce T, because you don't pass T to function argument.
auto total = foo<int>(1,2,3,4,5);
If we correctly put T manually as template argument - it will compile & work fine on G++7.1
Or, you can simply decltype required type from variadic argument.
something like this:
template <typename T, typename...Args>
T getHead(T&& arg, Args&&...) {
return arg;
}
template<typename... Args>
auto foo(Args... args)
{
using TYPE = typename std::decay<decltype(getHead(args...))>::type;
return std::array<TYPE, sizeof...(Args)>{ args... };
}
And yes, use std::array, return local C-style array is undefined behaviour.

std::enable_if an argument is a function?

Why does the following compile under C++11? (I know it won't link.) I would expect the std::enable_if test to fail since 1() is not a function type.
#include <type_traits>
template <typename Func, typename... Args>
typename std::enable_if<std::is_function<Func(Args...)>::value>::type
delegate(Func, Args...);
int main(void) {
delegate(1); // << Why does this line compile?
return 0;
}
Func is int, Args is empty, so Func(Args...) is int(), i.e., "function of () returning int".
Anything that is_function returns true for can't be the type of a by-value function parameter, so it's not obvious what you want to do.
I was trying to get delegate to only be callable when Func is a
function (preferably function pointer) that can be applied to Args...
Use expression SFINAE for that.
template <typename Func, typename... Args>
auto delegate(Func f, Args... args) -> decltype(f(args...), void());
Depending on what you actually want to do, you may want to std::move f and args.
The code you have written will always yield true. You probably meant std::is_function<Func>...
Though not sure, it seems like you do not need enable_if at all, and you'd better of with a simple
template <class R, class... ARGS>
R delegate2(R (*fun)(ARGS...), ARGS...);
However, if I am wrong and enable_if is a key to success in your case, here is how you can do this:
#include <type_traits>
template <typename Func, typename... Args>
typename std::enable_if<std::is_function<std::remove_pointer_t<Func>>::value>::type
delegate(Func, Args...);
void check(int);
int main(void) {
delegate(check, 10); // << good line compiles
delegate(10); // << this bad line does not
return 0;
}
Based on this comment:
I was trying to get delegate to only be callable when Func is a function (preferably function pointer) that can be applied to Args...
you're using the wrong type trait. To check if Func is callable with Args..., you need to construct an expression that would actually call an instance of Func with those arguments. For that, there's std::result_of_t (in C++14, it becomes SFINAE friendly):
template <typename Func, typename... Args,
class R = std::result_of_t<Func(Args...)>>
R delegate(Func, Args...);
Or, in C++11, just write that out with decltype and declval:
template <typename Func, typename... Args,
class R = std::declval<Func>()(std::declval<Args>()...)>
R delegate(Func, Args...);

C++ universal function caller

I'd want to implement a function caller that works just like the thread constructor. For example
std::thread second (bar,0);
will start a thread which calls bar with the single argument 0. I would like to do the same thing, but I do not know how.
For example, given:
void myFunc(int a){
cout << a << endl;
}
I would like:
int main() {
caller(myFunc,12);
}
to call myFunc with the parameter 12.
std::bind will make a callable object from any callable object with an arbitrary set of parameters, just as the thread constructor does. So just wrap that in a function that calls it:
template <typename... Args>
auto caller(Args &&... args) {
return std::bind(std::forward<Args>(args)...)();
}
Note that the auto return type requires C++14 or later. For C++11, you'll have to either return void, or specify the type:
auto caller(Args &&... args)
-> decltype(std::bind(std::forward<Args>(args)...)())
If all you want to do is call an arbitrary function with an arbitrary argument, that's just a template on both types:
template <typename Function, typename Arg>
void call_with_one(Function&& f, Arg&& arg) {
f(std::forward<Arg>(arg));
}
which you can expand to call with any number of args by making it variadic:
template <typename Function, typename... Arg>
void call_with_any(Function f, Arg&&... args) {
f(std::forward<Arg>(args)...);
}
Or really f should be a forwarding reference as well:
template <typename Function, typename... Arg>
void call_with_any(Function&& f, Arg&&... args) {
std::forward<Function>(f)(std::forward<Arg>(args)...);
}
Note that this will only work with functions and objects that implement operator(). If f is a pointer-to-member, this will fail - you will have to instead use std::bind as Mike Seymour suggests.