This snippet of code is taken from https://en.cppreference.com/w/cpp/utility/variant/visit
using var_t = std::variant<int, long, double, std::string>;
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
for (auto& v: vec) {
// 4. another type-matching visitor: a class with 3 overloaded operator()'s
// Note: The `(auto arg)` template operator() will bind to `int` and `long`
// in this case, but in its absence the `(double arg)` operator()
// *will also* bind to `int` and `long` because both are implicitly
// convertible to double. When using this form, care has to be taken
// that implicit conversions are handled correctly.
std::visit(overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
}, v);
}
Can someone explain what using Ts::operator()...; means here?
And in the following, what constructor is this calling? with the 3 lambda functions?
overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; }
}
I think the concrete overloaded instance is deriving from all 3 of these function types, and then the visitor is picking the right one to use depending on the type of the variant. Is that right?
I just don't fully understand this example.
It creates a struct template called overloaded which inherits from all its template arguments. Next, it pulls all declared operator() functions from its base classes into its own scope, so these all take part in overload resolution when a user calls operator() on an instance of the overloaded struct.
The ... uses parameter pack expansion to perform the same operation for all template arguments.
And in the following, what constructor is this calling? with the 3 lambda functions?
It’s not a constructor, it’s aggregate initialisation. In this case it uses class template argument deduction (CTAD) to deduce the template arguments of overloaded and initialise its base class instances. Minus the CTAD, the aggregate initialisation is identical to this case:
struct A {};
struct B {};
struct C : A, B {};
void f() {
C c{A{}, B{}};
}
In effect you’re creating an instance of the overloaded struct template, directly initialise an object for it with the given base class objects, and pass that to std::visit. The net effect is as if you had defined a struct with multiple operator() overloads (which is the normal way of using std::visit).
Related
I want to make a simple logger which automatically runs a function and returns its value.
The class is defined as:
template <typename R, typename... Args>
class Logger3
{
Logger3(function<R(Args...)> func,
const string& name):
func{func},
name{name}
{}
R operator() (Args ...args)
{
cout << "Entering " << name << endl;
R result = func(args...);
cout << "Exiting " << name << endl;
return result;
}
function<R(Args...)> func;
string name;
};
I want to pass the following simple add function to the logger:
int add(int a, int b)
{
cout<<"Add two value"<<endl;
return a+b;
}
By calling it this way:
auto caller = Logger3<int(int,int)>(add,"test");
However, it generates the following errors:
error: function returning a function
133 | Logger3(function<R(Args...)> func,
| ^~~~~~~
decorator.h:138:7: error: function returning a function
138 | R operator() (Args ...args)
| ^~~~~~~~
decorator.h:145:26: error: function returning a function
145 | function<R(Args...)> func;
There are 3 issues in your code:
The Logger3 class template requires R to be the return value of the function (and Args it's arguments).
(R is not a function type as implied by your attempt to instantiate Logger3).
Therefore instantiating the Logger3 in your case of a function that gets 2 ints and returns an int should be:
auto caller = Logger3<int, int, int>(add, "test");
Your Logger3 constructor should be public in order to invoke it from outside the class.
For efficiency reasons, you should use std::forward to forward the arguments from operator() to your function. This will avoid copy of the arguments (more significant in cases where their types are more complex than ints).
Note that in order for std::forward to work as expected, operator() has to be itself a variadic template using forwarding references (see below).
Complete fixed version:
#include <string> // std::string
#include <functional> // std::function
#include <utility> // std::forward, std::declval
#include <iostream> // std::cout
template <typename R, typename... Args>
class Logger3
{
public:
Logger3(std::function<R(Args...)> func,
const std::string& name) :
func{ func },
name{ name }
{}
// Template with forwarding references to avoid copies
// 'typename' arg is for SFINAE, and only enables if a
// function accepting 'Args...' can evaluate with 'UArgs...'
template <typename...UArgs,
typename = decltype(std::declval<R(*)(Args...)>()(std::declval<UArgs>()...))>
R operator() (UArgs&&...args)
{
std::cout << "Entering " << name << std::endl;
R result = func(std::forward<UArgs>(args)...);
std::cout << "Exiting " << name << std::endl;
return result;
}
private:
std::function<R(Args...)> func;
std::string name;
};
int add(int a, int b)
{
std::cout << "Add two value" << std::endl;
return a + b;
}
int main()
{
auto caller = Logger3<int, int, int>(add, "test");
auto res = caller(3, 4);
std::cout << "result: " << res << std::endl;
return 0;
}
Output:
Entering test
Add two value
Exiting test
result: 7
Demo: Godbolt.
A side note: better to avoid using namespace std - see here: Why is "using namespace std;" considered bad practice?.
You need to use template class partial specialization to get the type of R.
template <typename F>
class Logger3;
template <typename R, typename... Args>
class Logger3<R(Args...)>
{
// implementation details
};
which makes int match the template parameter R of the partial specialization when you explicitly specify Logger3<int(int,int)>.
I was looking at the article on std::variant http://en.cppreference.com/w/cpp/utility/variant/visit
The example contains essentially the following lines (sligtly modified by me):
template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
auto a = overloaded {
[](auto arg) { std::cout << arg << ' '; },
[](double arg) { std::cout << std::fixed << arg << ' '; },
[](const std::string& arg) { std::cout << std::quoted(arg) << ' '; },
};
The code basically uses each lambda function in a list as a base class for the struct overloaded. The first line pulls lambda's operator() into the scope of the struct. The second line uses class template argument deduction guides (C++17).
question
I do not understand the line #3 with the use of { } braces after the overloaded.
What kind of C++ mechanism works here?
Do we use the initialization list and convert it into a variadic template parameters or it is a kind of uniform/aggregate initialization? Is any actual constructor being called in this line?
Interestingly, the construction fails if I use ( ).
It's aggregate initialization, specifically of the direct public bases of the resulting type. Since C++17, the base subobjects don't have to be aggregates themselves, they are copy initialised from the elements of the initialiser list.
The following code does not compile:
#include <iostream>
#include <utility>
struct Foo
{
Foo() { std::cout << "Foo()" << std::endl; }
Foo(int) { std::cout << "Foo(int)" << std::endl; }
};
template <typename T>
struct Bar
{
Foo foo;
Bar(const Bar&) { std::cout << "Bar(const Bar&)" << std::endl; }
template <typename... Args>
Bar(Args&&... args) : foo(std::forward<Args>(args)...)
{
std::cout << "Bar(Args&&... args)" << std::endl;
}
};
int main()
{
Bar<Foo> bar1{};
Bar<Foo> bar2{bar1};
}
Compiler error suggest to me that compiler was trying to use variadic template constructor instead of copy constructor:
prog.cpp: In instantiation of 'Bar<T>::Bar(Args&& ...) [with Args = {Bar<Foo>&}; T = Foo]':
prog.cpp:27:20: required from here
prog.cpp:18:55: error: no matching function for call to 'Foo::Foo(Bar<Foo>&)'
Bar(Args&&... args) : foo(std::forward<Args>(args)...)
Why compiler does that and how to fix it?
This call:
Bar<Foo> bar2{bar1};
has two candidates in its overload set:
Bar(const Bar&);
Bar(Bar&); // Args... = {Bar&}
One of the ways to determine if one conversion sequence is better than the other is, from [over.ics.rank]:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence
S2 if
— [...]
— S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same
type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers
is more cv-qualified than the type to which the reference initialized by S1 refers. [ Example:
int f(const int &);
int f(int &);
int g(const int &);
int g(int);
int i;
int j = f(i); // calls f(int &)
int k = g(i); // ambiguous
—end example ]
The forwarding reference variadic constructor is a better match because its reference binding (Bar&) is less cv-qualified than the copy constructor's reference binding (const Bar&).
As far as solutions, you could simply exclude from the candidate set anytime Args... is something that you should call the copy or move constructor with SFINAE:
template <typename... > struct typelist;
template <typename... Args,
typename = std::enable_if_t<
!std::is_same<typelist<Bar>,
typelist<std::decay_t<Args>...>>::value
>>
Bar(Args&&... args)
If Args... is one of Bar, Bar&, Bar&&, const Bar&, then typelist<decay_t<Args>...> will be typelist<Bar> - and that's a case we want to exclude. Any other set of Args... will be allowed just fine.
While I agree that it's counter-intuitive, the reason is that your copy constructor takes a const Bar& but bar1 is not const.
http://coliru.stacked-crooked.com/a/2622b4871d6407da
Since the universal reference can bind anything it is chosen over the more restrictive constructor with the const requirement.
Another way to avoid the variadic constructor being selected is to supply all forms of the Bar constructor.
It's a little more work, but avoids the complexity of enable_if, if that's important to you:
#include <iostream>
#include <utility>
struct Foo
{
Foo() { std::cout << "Foo()" << std::endl; }
Foo(int) { std::cout << "Foo(int)" << std::endl; }
};
template <typename T>
struct Bar
{
Foo foo;
Bar(const Bar&) { std::cout << "Bar(const Bar&)" << std::endl; }
Bar(Bar&) { std::cout << "Bar(Bar&)" << std::endl; }
Bar(Bar&&) { std::cout << "Bar(Bar&&)" << std::endl; }
template <typename... Args>
Bar(Args&&... args) : foo(std::forward<Args>(args)...)
{
std::cout << "Bar(Args&&... args)" << std::endl;
}
};
int main()
{
Bar<Foo> bar1{};
Bar<Foo> bar2{bar1};
}
The "std-way" to solve this issue is to put a parameter of std::in_place_t first. That way you have a clear type to force the compiler to use the templated constructor when you want and to not let it match when you don't want. You could check the way it is done here https://en.cppreference.com/w/cpp/utility/optional/optional.
I am attempting to recreate the Observer pattern where I can perfectly forward parameters to a given member function of the observers.
If I attempt to pass the address of a member function which has multiple overrides, it cannot deduce the correct member function based on the arguments.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename Class>
struct observer_list
{
template<typename Ret, typename... Args, typename... UArgs>
void call(Ret (Class::*func)(Args...), UArgs&&... args)
{
for (auto obj : _observers)
{
(obj->*func)(std::forward<UArgs>(args)...);
}
}
std::vector<Class*> _observers;
};
struct foo
{
void func(const std::string& s)
{
std::cout << this << ": " << s << std::endl;
}
void func(const double d)
{
std::cout << this << ": " << d << std::endl;
}
};
int main()
{
observer_list<foo> l;
foo f1, f2;
l._observers = { &f1, &f2 };
l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);
return 0;
}
This fails to compile with template argument deduction/substitution failed.
Note that I had Args... and UArgs... because I need to be able to pass parameters which are not necessarily the same type asthe type of the function signature, but are convertible to said type.
I was thinking I could use a std::enable_if<std::is_convertible<Args, UArgs>> call to disambiguate, but I don't believe I can do this with a variadic template parameter pack?
How can I get the template argument deduction to work here?
The issue is here:
l.call(&foo::func, "hello");
l.call(&foo::func, 0.5);
For both lines, the compiler doesn't know which foo::func you are referring to. Hence, you have to disambiguate yourself by providing the type information that is missing (i.e., the type of foo:func) through casts:
l.call(static_cast<void (foo::*)(const std::string&)>(&foo::func), "hello");
l.call(static_cast<void (foo::*)(const double )>(&foo::func), 0.5);
Alternatively, you can provide the template arguments that the compiler cannot deduce and that define the type of func:
l.call<void, const std::string&>(&foo::func, "hello");
l.call<void, double >(&foo::func, 0.5);
Notice that you have to use double and not const double above. The reason is that generally double and const double are two different types. However, there's one situation where double and const double are considered as if they were the same type: as function arguments. For instance,
void bar(const double);
void bar(double);
are not two different overloads but are actually the same function.
In the current state of c++11 (say gcc 4.7.2), how should I choose between using a variadic-template or a std::initializer_list when I need a constructor that can take variable arguments?
A variadic template allows you providing arguments of different types, while an std::initializer_list is templated with the type of the argument. This means the type of all the elements in the list must be the same (or convertible to the underlying type, but no narrowing conversions are allowed). Depending on whether or not this is desirable for you, you may choose one or the other.
Also, a variadic template is usually the default choice if you need perfect forwarding, in that the syntactic form T&& can bind to both lvalue references and rvalue references, while a similar type deduction cannot be performed for initializer_list:
struct A
{
// Deduces T& for lvalue references, T for rvalue references, and binds to both
template<typename... Ts>
A(Ts&&...) { }
// This is an rvalue reference to an initializer_list. The above type deduction
// does not apply here
template<typename T>
A(initializer_list<T>&&) { }
};
Also notice, that a constructor accepting an initializer_list will be invoked by default when you use uniform initialization syntax (i.e. curly braces), even though another viable constructor exists. This may or may not be something you wish to have:
struct A
{
A(int i) { }
};
struct B
{
B(int) { }
B(std::initializer_list<A>) { }
};
int main()
{
B b {1}; // Will invoke the constructor accepting initializer_list
}
With a variadic template, the number of arguments is known during compilation (and accessible via sizeof...). With a std::initializer_list, the number of arguments is known only at runtime. So part of the decision depends on when you need or want to know how many arguments you have.
I recommend always chosing variadic templates and avoid std::initializer_list whenever possible.
This is how I would have implemented std::vector with C++11:
#include <iostream>
#include <vector>
struct exp_sequence {
template <typename... T>
exp_sequence(T&&...) {}
};
struct from_arglist_t {} from_arglist;
template <typename T>
class my_vector {
std::vector<T> data;
public:
my_vector(int n, T const& e) : data(n, e) {}
template <typename... Args>
my_vector(from_arglist_t, Args&&... args) {
data.reserve(sizeof...(Args));
exp_sequence{(data.push_back(std::forward<Args>(args)),1)...};
}
std::size_t size() { return data.size(); }
};
int main()
{
std::vector<int> v1{13, 13}; std::cout << v1.size() << '\n'; // 2
std::vector<int> v2(13, 13); std::cout << v2.size() << '\n'; // 13
my_vector<int> v3{13, 13}; std::cout << v3.size() << '\n'; // 13
my_vector<int> v4(13, 13); std::cout << v4.size() << '\n'; // 13
my_vector<int> v5(from_arglist, 13, 13); std::cout << v5.size() << '\n'; // 2
my_vector<int> v6{from_arglist, 13, 13}; std::cout << v6.size() << '\n'; // 2
}
The reason is as showed in main, using initializer_list in generic code can lead to different behaviour depending on which type of parentheses was chosen. There is also the possibility to silently change code by adding such an constructor.
Another reason are move-only types:
//std::vector<move_only> m1{move_only{}}; // won't compile
my_vector<move_only> m2{from_arglist, move_only{}}; // works fine