Use initializer list to create single item vector - c++

I have a function func which is overloaded to take either a std::vector<Obj> argument or a Obj argument.
#include <vector>
#include <iostream>
class Obj {
int a = 6;
};
void func(const std::vector<Obj>& a) {
std::cout << "here" << std::endl;
}
void func(const Obj& a) {
std::cout << "there" << std::endl;
}
int main() {
Obj obj, obj2;
func({obj});
func({obj, obj2});
}
Actual output:
there
here
Expected output:
here
here
It seems {obj} doesn't initialize a vector, but rather an object. I guess there is some priority order when it comes to which type it initializes. How do I control it precisely?
(Examples compiled with g++ (Ubuntu 8.3.0-6ubuntu1) 8.3.0.)
I found a possible duplicate (c++11 single element vector initialization in a function call), although my question remains unanswered:
I understand that {obj} can resolve to an object rather a vector of a single element and the former takes priority. But is there a way to use {} to create a single item vector (so that foo resolves to the std::vector overload)? I could create a vector explicitly but {} seems nicer.

As mentioned in the linked question duplicate (the original) there is no way to force the resolution in favour of the overload taking std::initializer_list.
The original signatures (using int to simplify):
void func(const std::vector<int> &a);
void func(const int &a);
Since I have encountered this many times I typically do this:
func(std::vector<int>{10});
I am not aware of any shorter way of doing this because using actual type std::initializer_list that would do the same is even more verbose. But on the birght side it at least makes what you are doing perfectly clear since {10} is really ambiguous if not accompanied with the type.

You can't.
Excerpt (from here, emphasis mine):
"[...] if the initializer list has a single element of type E and
either T is not a reference type or its referenced type is
reference-related to E, the object or reference is initialized from
that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization)"
Therefore, in your linked example, {obj} will "decay" to obj and the overload will resolve to void func(const Obj& a).
As stated in the other answer, you can explicitely call func(std::vector {obj}) to call the vector overload instead.

Related

Avoid Conversion from single element vector to primitive type

I have a problem with constant single element std::vector when pass through a function. C++ compiler automatically call wrong function when the std::vector variable contain a single element. This is though the policy of C++ design. However is there any explicit method to specify in such a case. Here are the examples of the problems
assume i have two overload functions both have the same name "foo"
void foo(const std::vector<int> A)
{
// do vector operator
printf("vector thing");
}
void foo(int a)
{
// do integer operator
printf("integer thing")
}
In general case both of these functions are called correctly
foo({1,2,3}); // print do vector thing
foo( 3 ); // print do integer thing
however from c++ rule. when call
foo({5}); // print do integer thing ( I want it to call as vector )
one of the methods is to create a variable
std::vector<int> B = { 5 };
in order to solve this problem.
I feel this method is a bit clumsy. Is there any method that can void the compiler to treat {5} as 5 and call foo(int a).
note:
here is the reference that explain what the problem is
c++11 single element vector initialization in a function call
You need another overload, taking std::initializer_list as a parameter:
void foo(std::initializer_list<int> A)
{
foo(std::vector<int>(A.begin(), A.end()));
}
If you always call this function by creating vectors directly with {...}, rather than using std::vector variables, then you can remove std::vector overload completely and operate directly on std::initializer_list.
No, because as of C++17 the rules explained on the linked answer still hold.
You can create a temporary instead of a variable, though.
foo(std::vector{5}); // C++17 with class type deduction
foo(std::vector<int>{5}); // older versions
One way to disambiguate the function call is to make the integer overload a function template:
template <class Int> void foo(Int a)
{
std::printf("generalized (maybe integer) thing\n");
}
This way, the invocation
foo({3});
will consider the non-templated function a better match while foo(3) instantiates and calls the function template. This because {3} is an std::initializer_list<int> with one element in the context of type deduction. You can also redirect to the original foo(int a) function like this:
void fooImpl(int a)
{
std::printf("integer thing\n");
}
template <class Int> void foo(Int&& a)
{
fooImpl(std::forward<Int>(a));
}
This refuses to compile when e.g. calling foo with an argument not convertible to an integer, which might be a desirable usage restriction. Also, it should be very unlikely that you encounter a performance overhead due to the forwarding intermediate function.

Why does this constructor create a "delegate cycle"? [duplicate]

Consider the code below:
#include <iostream>
#include <vector>
void f(std::vector<int> v) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
void f(int n) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
int main()
{
f({42}); // the int overload is being picked up
}
Live on Coliru
I was a bit surprised to realize that in this case the int overload is being picked up, i.e. the output of the program is:
void f(int)
with the warning
warning: braces around scalar initializer [-Wbraced-scalar-init] f({42});
Of course this happens only when I pass a 1-element list as an argument, otherwise the std::vector overload is being picked up.
Why is {42} treated like a scalar and not like a init-list? Is there any way of forcing the compiler to pick the std::vector overload (without explicitly constructing std::vector<int>{42}) even on 1-element lists?
PS: The std::vector has an init-list constructor
vector(std::initializer_list<T> init, const Allocator& alloc = Allocator());
see (7) from cppreference.
Braced initializer has no type, we can't say {42} is an int or std::initializer_list<int>. When it's used as an argument, special rules for overload resolution will be applied for overloaded function call.
(emphasis mine)
Otherwise, if the parameter type is not a class and the initializer list has one element, the implicit conversion sequence is the one required to convert the element to the parameter type
{42} has only one element with type int, then it's exact match for the overload void f(int). While for void f(std::vector<int>) a user-defined conversion is needed. So void f(int) will be picked up here.
Is there any way of forcing the compiler to pick the std::vector overload (without explicitly constructing std::vector<int>{42}) even on 1-element lists?
As a wordaround, you can put additional braces to force the compiler construct a std::initializer_list<int> and then pick up void f(std::vector<int>):
f({{42}});
LIVE
Forcing std::vector overload
int main()
{
f(std::vector<int>{42}); // the vector overload is being picked up now
}
Why isn't the vector(initializer_list) constructor being picked up?
Assume that another header declares a void f(std::set<int> v).
How would you like the compiler to react when faced with f({1}): construct a vector or construct a set?

Forcing std::vector overload instead of int overload on list with one element

Consider the code below:
#include <iostream>
#include <vector>
void f(std::vector<int> v) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
void f(int n) {std::cout << __PRETTY_FUNCTION__ << std::endl;}
int main()
{
f({42}); // the int overload is being picked up
}
Live on Coliru
I was a bit surprised to realize that in this case the int overload is being picked up, i.e. the output of the program is:
void f(int)
with the warning
warning: braces around scalar initializer [-Wbraced-scalar-init] f({42});
Of course this happens only when I pass a 1-element list as an argument, otherwise the std::vector overload is being picked up.
Why is {42} treated like a scalar and not like a init-list? Is there any way of forcing the compiler to pick the std::vector overload (without explicitly constructing std::vector<int>{42}) even on 1-element lists?
PS: The std::vector has an init-list constructor
vector(std::initializer_list<T> init, const Allocator& alloc = Allocator());
see (7) from cppreference.
Braced initializer has no type, we can't say {42} is an int or std::initializer_list<int>. When it's used as an argument, special rules for overload resolution will be applied for overloaded function call.
(emphasis mine)
Otherwise, if the parameter type is not a class and the initializer list has one element, the implicit conversion sequence is the one required to convert the element to the parameter type
{42} has only one element with type int, then it's exact match for the overload void f(int). While for void f(std::vector<int>) a user-defined conversion is needed. So void f(int) will be picked up here.
Is there any way of forcing the compiler to pick the std::vector overload (without explicitly constructing std::vector<int>{42}) even on 1-element lists?
As a wordaround, you can put additional braces to force the compiler construct a std::initializer_list<int> and then pick up void f(std::vector<int>):
f({{42}});
LIVE
Forcing std::vector overload
int main()
{
f(std::vector<int>{42}); // the vector overload is being picked up now
}
Why isn't the vector(initializer_list) constructor being picked up?
Assume that another header declares a void f(std::set<int> v).
How would you like the compiler to react when faced with f({1}): construct a vector or construct a set?

Is it safe to pass as argument a member of an object which is moving

#include <iostream>
#include <string>
#include <map>
struct A {
int n { 42 };
std::string s { "ciao" };
};
int main() {
A a;
std::map<std::string, A> m;
std::cout << "a.s: " << a.s << std::endl; // print: "a.s: ciao"
m.emplace(a.s, std::move(a)); // a.s is a member of a, moved in the same line
std::cout << "in map: " << m.count("ciao") << std::endl; // print: "in map: 1"
std::cout << "a.s: " << a.s << std::endl; // print: "a.s: " (as expected, it has been moved)
}
Is it safe to pass as an argument a member of a "moving" object? In this case, emplace seems to work: the map has the expected key.
Interesting. I think it's safe, for convoluted reasons. (For the record, I also consider it very bad style -- an explicit copy costs you nothing here, since it will be moved into the map.)
First of all, the actual function call is not a problem. std::move only casts a to an rvalue reference, and rvalue references are just references; a is not immediately moved. emplace_back forwards its parameters to a constructor of std::pair<std::string, A>, and this is where things get interesting.
So, which constructor of std::pair is used? It has rather many, but two are relevant:
pair(const T1& x, const T2& y);
template<class U, class V> pair(U&& x, U&&y);
(See 20.3.2 in the standard), where T1 and T2 are the template arguments of std::pair. As per 13.3, we end up in the latter with U == const T1& and V == T2, which makes intuitive sense (otherwise moving into a std::pair would be effectively impossible). This leaves us with a constructor of the form
pair(const T1& x, T2 &&y) : first(std::forward(x)), second(std::forward(y)) { }
as per 20.3.2 (6-8).
So, is this safe? Helpfully, std::pair is defined in some detail, including memory layout. In particular, it states that
T1 first;
T2 second;
come in this order, so first will be initialized before second. This means that in your particular case, the string will be copied before it is moved away, and you're safe.
However, if you were doing it the other way around:
m.emplace(std::move(A.s), A); // huh?
...then you'd get funny effects.
What happens when you call m.emplace(a.s, std::move(a)); is that you are passing an l-value reference to a.s and an r-value reference to a to the emplace function. Whether this is safe depends on the implementation of that function. If it first rips the guts out of the second argument and then tries to use the first argument, you'll likely have a problem. If it first uses the first argument and then rips the guts out of the second argument, no problem. Since this is a standard library function, you cannot rely on the implementation, so we cannot conclude that the code is safe.
To make it safe, you can make sure that a copy of the string is made before the contents of the string are moved out of a.
m.emplace(std::string(a.s), std::move(a));
Now you are passing an r-value reference to a temporary copy of a.s and an r-value reference to a to the emplace function. So, it's safe in this case, because std::map::emplace takes a set of universal references. If you would do the same with a function that takes it second argument by value, it's unsafe, because the move constructor could be called before the copy of a.s is made (since evaluation order of function arguments is unspecified). Knowing that, this code is probably too smart to be maintained.
So, the clearest code is something like:
auto key = a.s;
m.emplace(std::move(key), std::move(a));

How does boost::unordered_map.emplace(Args&&... args) work?

According to the documentation it:
Inserts an object, constructed with
the arguments args, in the container
if and only if there is no element in
the container with an equivalent key.
But the only objects which can be inserted into an unordered_map have type std::pair<Key const, Mapped>(because both a key and a value are needed for an object to be inserted), which is known to take a constructor with exactly two arguments. So why does it use the variadic function form? Surely there is something I am totally not understanding about this.
See this SO article on emplace_back vs. push_back. Essentially, it allows an object to be constructed from the arguments passed into it without needing to create the object to be passed in first. It saves on overhead by removing a copy construction which normally happens as the result of creating objects to be inserted.
So you can get away with this:
unordered_map<int,int> foo;
foo.emplace(4, 5);
instead of
foo.insert(std::make_pair(4, 5));
Even better, (and if I'm not mistaken), you can go through this route:
struct Bar{
int x,y;
Bar(int _x, int _y) : x(_x), y(_y){}
};
unordered_map<int,Bar> baz;
baz.emplace(4, 5, 6);
And taken from the Wiki on C++0x:
Due to the nature of the wording of rvalue references, and to some modification to the wording for lvalue references (regular references), rvalue references allow developers to provide perfect function forwarding. When combined with variadic templates, this ability allows for function templates that can perfectly forward arguments to another function that takes those particular arguments. This is most useful for forwarding constructor parameters, to create factory functions that will automatically call the correct constructor for those particular arguments.
Which works in the following manner:
template<typename TypeToConstruct> struct SharedPtrAllocator {
template<typename ...Args> std::shared_ptr<TypeToConstruct> construct_with_shared_ptr(Args&&... params) {
return std::shared_ptr<TypeToConstruct>(new TypeToConstruct(std::forward<Args>(params)...));
}
}
Again, shamelessly stolen from the Wiki article mentioned above.
Now that the C++ Standard Library has integrated that part of Boost:
From http://en.cppreference.com
#include <iostream>
#include <utility>
#include <tuple>
#include <unordered_map>
int main(){
std::unordered_map<std::string, std::string> m;
// uses pair's piecewise constructor
m.emplace(std::piecewise_construct,
std::forward_as_tuple("c"),
std::forward_as_tuple(10, 'c'));
for (const auto &p : m) {
std::cout << p.first << " => " << p.second << '\n';
}
}
std::piecewise_construct is a constant that leaves no ambiguity about how the arguments will be used
The first tuple will be used to construct the key
The second to construct the value