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)...};
}
}
Related
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.
In the std::optional::emplace docs there is an overload that accepts std::initializer_list:
template< class U, class... Args >
T& emplace( std::initializer_list<U> ilist, Args&&... args );
provided that
std::is_constructible<T, std::initializer_list&, Args&&...>::value is true
I thought that it might be used to emplace POD types, but apparently this is not how it works (in other SO topics it was explained that emplace functions are using () syntax instead of {}):
struct A
{
int x;
int y;
int z;
};
int main()
{
A normalA{1, 2, 3}; // this is OK
std::cout << std::is_constructible<A, std::initializer_list<int>&, int, int, int>::value << std::endl; // false
std::cout << std::is_constructible<A, std::initializer_list<int>&>::value << std::endl; // false
std::optional<A> optA;
// optA.emplace({1, 2, 3}); // this is NOK
optA.emplace(A{1, 2, 3}); // I can walk it around with copy-ctor
}
I can write the constructor accepting initializer_list:
struct B
{
B(std::initializer_list<int> l) {/* some impl here */}
int x;
int y;
int z;
};
and then call emplace like this:
std::optional<B> optB;
optB.emplace({1, 2, 3});
but shouldn't first emplace overload T& emplace( Args&&... args ); be enough for that?
I thought that it might be useful for the array types, but std::optional<int[]> xxx; does not compile anyway.
Can you please provide some example where second std::optional::emplace overload is used.
but shouldn't first emplace overload T& emplace( Args&&... args ); be enough for that?
It isn't because a braced-init-list, i.e. {1, 2, 3} has no type. Because it has no type, there is nothing to compiler can do to deduce what Args should be. We need to have an overload that explicitly takes a std::initializer_list so that we can avoid the compiler not being able to deduce what the braced-init-list should be considered as.
The following template definition
template <typename Func, typename ReturnType, typename... Arguments>
class Command
{
public:
Command(Func f) : m_func(f) { }
ReturnType operator()(Arguments... funcArgs) { return m_func(funcArgs...); }
private:
Func m_func;
};
gives an error message with gcc 4.7.3 (error: field 'Command::m_func' invalidly declared function type) when instantiated with the following test code:
void testFunction(int i, double d)
{
std::cout << "TestFunctor::operator()(" << i << ", " << d << ") called." << std::endl;
}
int main()
{
void (&fRef)(int, double) = TestFunction;
Command<void(int, double), void, int, double> testCommand(fRef);
}
The error message also occurs if I pass TestFunction without the address-of operator into the testCommand constructor, but disappears if I pass either an explicitly named function pointer or use the address-of operator to pass the parameter. I'm under the impression that this code should work given Chapter 5 of Modern C++ Design.
What is the reasoning behind not being able to store a reference to a function, but function pointers work fine? Are there any workarounds that would allow this to compile without losing support for being able to pass functors as arguments to Command's constructor as well?
Changing one line could fix it:
Command<void(*)(int, double), void, int, double> testCommand(fRef);
The difference is, you're passing a function pointer now, instead of a function type. (Functions aren't copyable, but pointers are).
The reference fRef decays to a function pointer when you pass it.
I wouldn't suggest using std::function if performance mattered.
See it live on Coliru
Note that with a little rewriting, you can make it all work much nicer:
int main()
{
auto command = make_command(testFunction);
command(1, 3.14);
}
To do this, I'd suggest changing the Command template to be:
template <typename Func>
class Command
{
Func m_func;
public:
Command(Func f) : m_func(f) { }
template <typename... A> auto operator()(A... args) const
-> decltype(m_func(args...))
{ return m_func(args...); }
};
And now you can have type-deduction on the Func template parameter by having a factory function:
template <typename Func> Command<Func> make_command(Func f)
{
return Command<Func>(f);
}
See this approach live on Coliru too. Of course, the output it the same:
TestFunctor::operator()(1, 3.14) called.
C++11 offers an std::function template. You don't have to mess with function pointers.
You can pass those by reference, copy them, move them and they can even be used to store lambdas:
std::function<void()> func = []() { std::cout << "Hi" << std::endl; };
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
Given the following callable object:
struct callable : public std::unary_function <void, void>
{
void
operator()() const
{
std::cout << "hello world" << std::endl;
}
};
a std::tr1::reference_wrapper<> calls through it:
callable obj;
std::tr1::ref(obj)();
Instead, when the operator() accepts an argument:
struct callable : public std::unary_function <int, void>
{
void
operator()(int n) const
{
std::cout << n << std::endl;
}
};
std::tr1::bind accepts a reference_wrapper to it as a callable wrapper...
callable obj;
std::tr1::bind( std::tr1::ref(obj), 42 )();
but what's wrong with this?
std::tr1::ref(obj)(42);
g++-4.4 fails to compile with the following error:
test.cpp:17: error: no match for call to ‘(std::tr1::reference_wrapper<const callable>) (int)’
/usr/include/c++/4.4/tr1_impl/functional:462: note: candidates are: typename std::tr1::result_of<typename std::tr1::_Function_to_function_pointer<_Tp, std::tr1::is_function::value>::type(_Args ...)>::type std::tr1::reference_wrapper<_Tp>::operator()(_Args& ...) const [with _Args = int, _Tp = const callable]
The implementation of tr1 reference_wrapper of g++-4.4 is equipped with the following operator:
template<typename... _Args>
typename result_of<_M_func_type(_Args...)>::type
operator()(_Args&... __args) const
{
return __invoke(get(), __args...);
}
It takes arguments by reference. Hence the reference_wrapper cannot be invoked passing an r-value argument:
std::tr1::ref(obj)(42);
instead:
int arg = 42;
std::tr1::ref(obj)(arg);
works just fine.
std::tr1::bind( std::tr1::ref(obj), 42 )()
works because bind takes the arguments by copy.
What makes you sure there's anything wrong with it? I believe this should work:
#include <functional>
#include <iostream>
struct callable : public std::unary_function <int, void>
{
void
operator()(int n) const
{
std::cout << n << std::endl;
}
};
int main() {
callable obj;
std::tr1::ref(obj)(42);
return 0;
}
At least with MS VC++ 9, it compiles and executes just fine, and offhand I can't see any reason it shouldn't work with other compilers as well.
Edit: Doing some looking at TR1, I take that back. It works with VC++ 9, but I don't think it's really required to work. VC++ 9 doesn't support variable template arguments, so they're supporting this via overloading. Rather deeply buried (<functional> includes <xawrap>, which includes <xawrap0> [which, in turn, includes <xawrap1>]) is code to generate reference and (importantly) reference to const variants for up to 10 arguments. It's almost certainly the inclusion of the reference to const variants that allows this to work.
First of all, the use of std::unary_function for a null-ary function looks odd. "unary" = takes one argument. I'm not sure whether it's okay to use ArgType=void.
Secondly, you have it backwards. The first template parameter is about the argument type and the second one is about the return type. So, your unary function object should be defined like this:
struct callable : public std::unary_function<int,void>
{
void operator()(int n) const
{
std::cout << n << std::endl;
}
};