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.
Related
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).
I have a struct that contains a variant.
I want to write a member function for that struct that should run code depending on which type variant currently holds.
However, I have issues making it compile.
I don't want to use more "template shenanigans" like using a separate struct to define operator(T&) since it pollutes the syntax even more.
Here is an example:
struct Data {
std::variant<int, double> var;
//Into this function,multiple lambdas should be passed for cases that the user wants to handle
template<typename ... Funcs>
void apply(Funcs&&... funcs) {
std::visit(std::forward<Funcs>(funcs)...,var);
}
};
int main() {
Data d;
d.var = 4;
//variant holds int and lambda provided that takes int&, execute it:
d.apply([](int& i){
std::cout << "I am Int Poggers" << std::endl;
});
d.var = 0.0;
//variant holds double but no lambda passed that takes a double, hence nothing happens:
d.apply([](int& i){
std::cout << "I am Int Poggers" << std::endl;
});
}
and I even don't know what the compiler wants from me:
https://godbolt.org/z/oM4584anf
Your problem is that std::visit() needs a "visitor" that must handle every type of the std::variant.
However, I have issues making it compile. I don't want to use more "template shenanigans" like using a separate struct to define operator(T&) since it pollutes the syntax even more.
There is nothing complicated.
You can simply add a trivial struct (with deduction guide) as follows (and as proposed in the cppreference std::visit() page)
template<class... Ts> struct overloaded : Ts...
{ using Ts::operator()...; };
template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
Then, given that you want that your std::visit() return void, you can add, in your apply() method, a generic-do-nothing lambda
template<typename ... Funcs>
void apply(Funcs&&... funcs) {
std::visit(overloaded{ // <-- pass through overloaded
[](auto const &){}, // <-- and add this generic lambda
std::forward<Funcs>(funcs)...},var);
}
Now the first apply() call
d.apply([](int& i){
std::cout << "I am Int Poggers" << std::endl;
});
should compile calling the supplied lambda because is a better match (given that d contains an int) and the second call compile calling the do-nothing generic lambda, because the generic lambda is a better match for a double.
At the end of this video (starting at 15:57) there is advice on how to use almost uniform initialization in C++17: video here
The gist goes like this: use always direct initialization auto a{...}; and MyType a{...};
Do not use copy initialization = {...} for your types.
#include <iostream>
struct MyType {
explicit MyType(std::initializer_list<int>) {
std::cout << "Called std::initializer_list<int>" << std::endl;
}
explicit MyType(int) {
std::cout << "Called int." << std::endl;
}
MyType(int, int, int) {
std::cout << "Called int, int, int" << std::endl;
}
};
int main() {
MyType calls_init_list{10}; //Calls initializer_list<int>
MyType calls_init_list_2{10, 20}; //Calls initializer_list<int>
MyType calls_init_list_3{10, 20, 30}; //Calls initializer_list<int>
MyType compile_error = {10, 20, 30}; //Compile error
}
If I remove explicit from the first constructor it will call the 4th call also with initializer_list<int>
What changes should I need for being able to call (int) and (int, int, int) following the rule in the video?
Is it even possible to call the other constructors in the presence of the initializer list constructor?
any design recommendations to avoid abandoning the general rule adviced in the video? It would be nice to finally have something that makes sense, C++ initialization is the most terrible part of it probably.
What changes should I need for being able to call (int) and (int, int, int) following the rule in the video?
Remove the initializer_list<int> constructor. That is the only way to make it work.
Is it even possible to call the other constructors in the presence of the initializer list constructor?
Yes, so long as the types in the braced-init-list cannot match those in the any initializer_list<T> constructors. They always have primacy.
Hence why it's derisively called "almost uniform initialization".
The typical solution is to add some tag type to the non-initializer_list constructors:
struct tag_t {};
constexpr inline tag_t tag;
struct MyType {
explicit MyType(std::initializer_list<int>) {
std::cout << "Called std::initializer_list<int>" << std::endl;
}
MyType(tag_t, int) {
std::cout << "Called int." << std::endl;
}
MyType(tag_t, int, int, int) {
std::cout << "Called int, int, int" << std::endl;
}
};
int main() {
MyType three_int = {tag, 10, 20, 30}; //Calls 3-`int` constructor
}
any design recommendations to avoid abandoning the general rule adviced in the video?
Well, considering that the "general rule" is not a good rule (his slide contains the quintessential counter-example: try to call the size+value version of vector<int> with braces), it's better to abandon it. Minor quibbles about what auto a{2}; translates into are irrelevant next to being literally incapable of calling some constructors.
In your case, to call MyType(int, int, int) or explicit MyType(int), you have to use () syntax instead of {}.
Basically, I don't think it's a good idea to always use {} syntax. For example, as of C++17, all emplace methods in the standard library are using () internally instead of {}. For example, the code
std::vector<std::vector<int>> vv;
vv.emplace_back(2, 1);
emplaces <1, 1> not <2, 1>. That's also why standard containers do not support emplace construction of aggregate types.
In my opinion, genuine uniform initialization that you can stick to is one that performs () initialization if possible, and falls back to {} otherwise (e.g., for aggregate types). Also see this. Possible implementation:
template <typename...>
struct paren_initable: std::false_type {};
template <typename T, typename... Us>
struct paren_initable<decltype((void)T(std::declval<Us>()...)), T, Us...>
: std::true_type {};
template <typename T, typename... Us>
inline constexpr bool paren_initable_v = paren_initable<void, T, Us...>::value;
template <typename T, typename... Us>
T emplace(Us&&... us) {
if constexpr (paren_initable_v<T, Us...>) {
return T(std::forward<Us>(us)...);
}
else {
return T{std::forward<Us>(us)...};
}
}
Consider a class which inherits from a std container with a template constructor which calls the underlying constructor of the container. This template constructor works for the simple copy and move constructor but not for the initializer_list ctor.
template<typename container_T>
class test : public container_T {
public:
using container_type = container_T;
test() {}
// templated constructor
template<typename T>
test(T t)
: container_T(t) {}
// without this it won't compile
test(std::initializer_list<typename container_T::value_type> l)
: container_T(l) {}
};
int main() {
test<std::deque<int>> vdi1;
test<std::deque<int>> vdi2({1,2,3,4,5,6,7,8,9});
std::cout << "vdi2 before:" << std::endl;
for(auto it : vdi2)
std::cout << it << std::endl;
test<std::deque<int>> vdi3(std::move(vdi2));
std::cout << "vdi2 before:" << std::endl;
for(auto it : vdi2)
std::cout << it << std::endl;
std::cout << "vdi3 before:" << std::endl;
for(auto it : vdi3)
std::cout << it << std::endl;
return 0;
}
If I remove the initializer_list constructor vdi2 won't compile. So my question: Why is the initializer_list not deduced by the template constructor? And is it possible to do so?
why is the initializer_list not deduced by the templated constructor?
The reason is that {1,2,3,4,5,6,7,8,9} is just a synctatic construct that doesn't have a type. Therefore, the compiler cannot deduce a type T for this synctatic construct and the first constructor fails.
However, by special Standard rules std::initializer_list<T> (among other things) can be construct from this synctatic construct and T can be deduced to int. Hence the second constructor works.
By constrast with function template argument type deduction, with
auto x = {1,2,3,4,5,6,7,8,9};
the compiler sets the type of x to be std::initializer_list<int>. There are also special Standard rules that says it must be so. Strictly speaking this is not type deduction because, as said above, {1,2,3,4,5,6,7,8,9} doesn't have a type to be deduced. (The only type deduction happening here is T = int in std::initializer_list<T>.) Here the compiler chooses (it doesn't deduce) the type of x to be std::initializer_list<int>. In any case, there's no harm to use the abuse of language of saying that the type of x is deduced to std::initializer_list<int>.
Finally, as DyP said in the comments, what you probably want is inheriting all constructors (not only those taking one argument) from the base container class. You can do this by removing all the constructors that you currently have and add just this line to test:
using container_type::container_type;
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