MSVC 2019 allow me to define a lambda like this, where the template argument is not used in the parameter list:
auto foo = []<bool B>() {
return B;
};
However, it gives syntax error when trying to call it like this?
foo<false>();
What is the correct way to call a lambda with a non-parameter template argument?
The template parameter is used with operator() of lambda.
(since C++20) If the lambda definition uses an explicit template parameter list, that template parameter list is used with operator().
You can specify the non-type template argument for operator(), call the lambda in an unnormal style as:
foo.operator()<false>();
foo is a function object. It has an operator(). The template on a lambda is on that operator, not on the object name itself.
foo<T> attempts to give the name foo a template argument T; that isn't valid here. foo<T>() gives the template argument to foo, then calls () on the result. Again, not what is going on here.
A template variable would work the way you want this to:
template<bool B>
auto foo = []() {
return B;
};
such variables only work in global scope, but here foo<true>() would work.
As a lambda is actually a compiler-generate class:
struct some_secret_name {
template<bool B>
auto operator()() const {
return B;
}
};
some_secret_name foo;
then the "right" way to call it is the ugly syntax:
foo.operator()<true>();
this, honestly, sucks.
You can get slightly better syntax like this:
template<class T> struct tag_t {using type=T;};
template<class T> constexpr tag_t<T> tag_v={};
template<auto v> using value_t = std::integral_constant<std::decay_t<decltype(v)>, v>;
template<auto v> constexpr value_t<v> value_v={};
then we can do
auto foo = []<bool B>(value_t<B>) {
return []{
return B;
};
};
now the syntax is:
foo(value_v<false>)();
which looks a bit less cludgy, if overly magical.
foo is now a lambda that makes lambdas, the outer lambda takes your template parameters as function arguments, and the inner lambda is the body.
Related
Trying to pass a lambda function to a template factory function which is templated on the function arguments of the passed function leads gcc-10.2.0 to report no matching function for call to ‘test(main()::<lambda(int, double)>)’.
It does seem to work when I add a + in front of the lambda function forcing the conversion to a function pointer, but I don't see why that would be necessary. Why does the conversion not happen automatically? Is there any way to make this work?
I have also tried std::function<void(TArgs...)> test_func as argument in the declaration of make_test, however that gives me the same no matching function for call error.
#include <iostream>
template <typename... TArgs>
struct test_type {
test_type(void(TArgs...)) {};
};
template <typename... TArgs>
test_type<TArgs...> make_test(void test_func(TArgs...)) {
return test_type<TArgs...>{test_func};
}
int main() {
auto test_object = make_test([](int a, double b) { std::cout << a << b << "\n"; });
return 0;
}
Edit
I was wondering if there maybe is some way to make it work with type traits. Something like the following. Although I know of no way to get the argument list from the template parameter.
template <typename F>
test_type<get_arg_list<F>> make_test(std::function<F>&& f) {
return test_type<get_arg_list<F>>{std::forward(f)};
}
In order to support a variety of callables being passed to your factory (e.g., a stateful lambda or a function pointer), your test_type constructor should accept some kind of type-erased function type like std::function<void(int, double)>:
template<class... TArgs>
struct test_type {
test_type(std::function<void(TArgs...)>) {};
};
Afterwards it's just a matter of writing the boilerplate to handle the following callables being passed to make_test
a regular function pointer
a lambda (struct with a operator()(...) const)
a mutable lambda or a user defined callable without a const operator() function
Here is one approach using type traits:
Start with a base class that we'll specialize for each scenario:
template<class T, class = void>
struct infer_test_type;
(This is a common setup for the voider pattern. We can do this with concepts and constraints, but I'm feeling too lazy to look up the syntax, maybe later)
Regular function pointer specialization
template<class Ret, class... Args>
struct infer_test_type<Ret(*)(Args...)>
{
using type = test_type<Args...>;
};
Now we can write a templated alias for simplicity:
template<class T>
using infer_test_type_t = typename infer_test_type<T>::type;
And we can verify that it works like so:
void foo(int, double){}
// ...
static_assert(std::is_same_v<infer_test_type_t<decltype(&foo)>, test_type<int, double>>);
We can use the type trait for our make_test function like so:
template<class T>
auto make_test(T&& callable) -> infer_test_type_t<T>
{
return infer_test_type_t<T>{std::forward<T>(callable)};
}
Now it's just a matter of covering our other two scenarios.
Callable objects
these will have operator() (either const or not)
I'll start with a top level trait to detect the presence of operator() and feed the type of operator() into another trait.
The top level trait:
// if T is a callable object
template<class T>
struct infer_test_type<T, std::void_t<decltype(&T::operator())>>
{
using type = typename infer_test_type<decltype(&T::operator())>::type;
};
You see that internally it's calling back into another infer_test_type specialization that I haven't shown yet; one that is specialized for operator(). I'll show the two specializations now:
// if operator() is a const member function
template<class T, class Ret, class... Args>
struct infer_test_type<Ret(T::*)(Args...) const>
{
using type = test_type<Args...>;
};
// if operator() is non-const member function
template<class T, class Ret, class... Args>
struct infer_test_type<Ret(T::*)(Args...)>
{
using type = test_type<Args...>;
};
(We could probably combine these two if we wanted to be a little bit smarter and lop off any const at the high level before calling down, but I think this is more clear)
And now we should be able to infer an appropriate test_type for non-generic callables (no generic lambdas or templated operator() functions):
a test with a non-const operator():
struct my_callable
{
void operator()(int, double) // non-const
{
}
};
// ...
static_assert(std::is_same_v<infer_test_type_t<my_callable>, test_type<int, double>>);
And a test with your lambda:
auto lambda = [](int a, double b) { std::cout << a << b << "\n"; };
static_assert(std::is_same_v<infer_test_type_t<decltype(lambda)>, test_type<int, double>>);
Putting it all together
For your simple (non-capturing, non-generic lambda) example it's quite straightforward:
make_test([](int a, double b) { std::cout << a << b << "\n"; });
Demo
Is it possible for a class method to be called somehow and automatically deduce templated arguments?
I'm trying to create an object with a create class method that calls a private constructor.
I'm doing so in order to make sure the created instance is allocated on the heap.
The class in templated and I'd like the user not having to know the types (can be complicated with rvalue reference to lambdas).
Here's a toy example of what I'd like to achieve (I know the constructor is public here):
template <typename T>
class hello
{
public:
hello(T t){}
static std::shared_ptr<hello<T>> create(T t)
{
return std::make_shared<hello<T>>(t);
}
};
template <typename T>
auto create_hello(T t)
{
return std::make_shared<hello<T>>(t);
}
void test_hello()
{
auto h1 = hello(3); // ok
auto h2 = std::make_shared<hello<int>>(3); // ok
auto h3 = create_hello(3); // ok
auto h4 = hello<int>::create(3); // ok
auto h5 = hello::create(3); // Compile error: 'hello' is not a class, namespace, or enumeration
}
Is there a way to invoke the static create method and having the template parameters automatically deduced like when calling the constructor directly?
No, this is not currently possible. There are only a few contexts in which the template arguments of a class can be deduced:
any declaration that specifies initialization of a variable and variable template, e.g.
hello h(3);
new-expressions, e.g.
auto h = new hello{3};
function-style cast expressions, e.g.
auto h = hello(3);
and from c++20:
the type of a non-type template parameter
In this expression:
hello::create(3)
the template argument needs to be deduced, since it's not specified, but none of the above contexts apply, since there is no rule for deduction of a template parameter from the invocation of a static member function.
You are mixing up two different things:
Constructor template deduction and
function template deduction.
Whereas auto h1 = hello(3) works since, it makes use of constructor template deduction, but since create is not a constructor, the template parameter cannot be deduced from the parameter.
To solve that, you have to source out your create function:
template <class U>
static std::shared_ptr<hello<T>> make_hello(U t)
{
return std::make_shared<hello<U>>(t);
}
Edit:
If you want have the constructor private, just friend the function:
template <typename T>
class hello
{
hello(T t){}
public:
template <class U>
friend std::shared_ptr<hello<U>> make_hello(U t);
};
template <class T>
std::shared_ptr<hello<T>> make_hello(T t)
{
return std::shared_ptr<hello<T>>(new hello<T>(t));
}
Why does the first call not compile?
auto get1 = []<int B>() { return B; };
auto get2 = []<typename B>(B b) { return b; };
int main()
{
get1<5>(); // error: no match for operator<
get2(5); // ok
}
The reason I use this, is an expression repeated many times in code.
Of course I can use a real function template, but just I am curious WHY.
This is easier to understand if you consider what the equivalent class type looks like to your get1:
struct get1_t {
template <int B> operator()() const { return B; }
};
get1_t get1;
get1<5>(); // error
You're trying to provide an explicit template parameter to the call operator, but syntactically you're doing what looks like providing template parameters for get1 itself (i.e. as if get1 were a variable template). In order to provide the template parameter for the call operator, you have to do that directly:
get1.operator()<5>(); // ok
Or restructure the call operator to take something deducible:
template <int B> struct constant { };
get1(constant<5>{});
Or restructure the whole thing to actually be the variable template that it looks like it is:
template <int B>
auto get1 = [] { return B; };
Now, get1<5> is itself a lambda, that you're invoking. That is, rather than a lambda with a call operator template we have a variable template lambda that is itself not a template.
Why does the first call not compile?
auto get1 = []<int B>() { return B; };
auto get2 = []<typename B>(B b) { return b; };
int main()
{
get1<5>(); // error: no match for operator<
get2(5); // ok
}
The reason I use this, is an expression repeated many times in code.
Of course I can use a real function template, but just I am curious WHY.
This is easier to understand if you consider what the equivalent class type looks like to your get1:
struct get1_t {
template <int B> operator()() const { return B; }
};
get1_t get1;
get1<5>(); // error
You're trying to provide an explicit template parameter to the call operator, but syntactically you're doing what looks like providing template parameters for get1 itself (i.e. as if get1 were a variable template). In order to provide the template parameter for the call operator, you have to do that directly:
get1.operator()<5>(); // ok
Or restructure the call operator to take something deducible:
template <int B> struct constant { };
get1(constant<5>{});
Or restructure the whole thing to actually be the variable template that it looks like it is:
template <int B>
auto get1 = [] { return B; };
Now, get1<5> is itself a lambda, that you're invoking. That is, rather than a lambda with a call operator template we have a variable template lambda that is itself not a template.
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
}