How to use second overload of std::optional<T>::emplace - c++

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.

Related

Variable list of pairs as function argument [duplicate]

Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:
foo({1, 1} , {2, 2.2}, {3, "3"}); // 'first' is always `int`
However, I couldn't figure out a syntax for How to define foo() using variadic templates?
This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:
template<typename... Args>
void foo (Args&&... args) { ... }
template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});
For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:
const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };
Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }
If your intention is call foo() in this way
foo({1, 1} , {2, 2.2}, {3, "3"});
using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.
If you can give up the curly braces, so calling foo() in this way,
foo(1, 1 , 2, 2.2, 3, "3");
and constructing pair inside foo(), you can do something like
#include <string>
#include <utility>
void foo ()
{
// do nothing ?
}
template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
{
std::pair<int, T2> p { v1, v2 };
// do something with p
foo(vs...);
}
int main()
{
foo(1, 1, 1, 2.2, 1, std::string("3"));
return 0;
}
But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()
foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
std::make_pair(1, std::string("3")));
You can simply use this signature:
template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}
or
template <typename ...Args> using pair = std::pair<int, Args...>;
Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string.
with overloading the () operator can we write:
#include <iostream>
#include <vector>
#include <sstream>
struct Function{
template <typename T>
Function& operator()(int a, T b){
ss << b;
list.push_back(ss.str());
ss.str("");
return *this;
}
std::vector<std::string> get_string(){
return list;
}
std::vector<std::string> list;
std::stringstream ss;
};
int main(){
Function foo;
for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
{
std::cout << s << std::endl;
}
}
Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.
template<typename DataType, template<class, class> typename ...Pair>
void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work
Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.

std::initializer_list rvalue/literal as parameter of (variadic) template

Please help me to understand why a std::initializer_list literal can't be deduced as a template parameter? AFAIK, there's no notion of an init-list literal in the language yet, but then why/how does this work?
auto il = { 1, 2, 3, 4, 5 };
Here is my code:
import <iostream>;
import <string>;
import <vector>;
template <typename T>
constexpr auto type_name() {
std::string_view name, prefix, suffix;
#ifdef __clang__
name = __PRETTY_FUNCTION__;
prefix = "auto type_name() [T = ";
suffix = "]";
#elif defined(__GNUC__)
name = __PRETTY_FUNCTION__;
prefix = "constexpr auto type_name() [with T = ";
suffix = "]";
#elif defined(_MSC_VER)
name = __FUNCSIG__;
prefix = "auto __cdecl type_name<";
suffix = ">(void)";
#endif
name.remove_prefix(prefix.size());
name.remove_suffix(suffix.size());
return name;
}
template< typename T >
int SAI_BF_helper(T&&) { return 0; }
int SAI_BF_helper(int i) { return i; }
// function that take any number parameters of any type and then return sum of all ints using binary left folding expressions
template< typename ... Types >
¡int SumAllInts_BinaryLeftFold(Types ... args)
{
return (0 + ... + SAI_BF_helper(args));
}
template< typename T >
void PrintTypeName(T&& t)
{
std::cout << type_name< decltype( std::forward< T >(t) )> () << std::endl;
}
// if this overload is removed then 'PrintTypeName({1,2,3});' code will not compile
template< typename T >
void PrintTypeName( std::initializer_list< T >&& t)
{
std::cout << type_name< decltype(std::forward< std::initializer_list< T > >(t))>() << std::endl;
}
int main()
{
std::vector< int > numbers{ 1, 2, 3 };
auto il = { 1, 2, 3, 4, 5 };
PrintTypeName(numbers); // output: class std::vector<int,class std::allocator<int> >&
PrintTypeName(il); // output: class std::initializer_list<int>&
PrintTypeName({1,2,3}); // output: class std::initializer_list<int>&&
std::cout << SumAllInts_BinaryLeftFold() << std::endl; // 0
std::cout << SumAllInts_BinaryLeftFold("", 0, 1, 2.2, 'a', "char*", 10, std::string("str"), numbers, il) << std::endl; // 11
//std::cout << SumAllInts_BinaryLeftFold( 1, {1, 2}) << std::endl; // MSVC error message: 'initializer list': is not a valid template argument for 'Types'
}
The short answer: initializer lists (the language mechanic, not the type) are magic. No, really.
The C++ standard has explicit wording for initializer-list syntax to produce a std::initializer_list object in certain circumstances such as constructing an auto-parameter, but an initializer list expression is not itself always a std::initializer_list; it depends on the circumstances.
This distinction is needed because the same syntax may be used in place of implicit construction for cases like parameters. For example, consider the following code:
auto do_something(Person) -> void;
...
do_something({}); // Calls Person() -- not Person(std::initializer_list<U>)
In this, {} isn't meant to be a std::initializer_list object; it's an implicit default-construction of Person.
Because these cases exist, deducing the exact type of a brace-enclosed initializer list is not so straight-forward. For example:
template <typename T>
auto do_something(T) -> void;
do_something({1}) -> void;
In the above case, what should T be? Is it do_something<std::initializer_list<int>>? do_something<int> with int{1}? What if this were {1, 2U}? It can get complicated.
There is no such thing as a "std::initializer_list literal". {1, 2, 3} is a braced-init-list; a syntactic construct with no type that can be used to initialize many types. There are a few specific situations (see below) where a std::initializer_list will be implicitly created from a braced-init-list, but they are not the same thing.
From cppreference :
A std::initializer_list object is automatically constructed when:
a braced-init-list is used to list-initialize an object, where the corresponding constructor accepts an std::initializer_list parameter
a braced-init-list is used as the right operand of assignment or as a function call argument, and the corresponding assignment operator/function accepts an std::initializer_list parameter
a braced-init-list is bound to auto, including in a ranged for loop
auto il = {1, 2, 3, 4, 5};
This is one of the situations when a std::initializer_list will be implicitly created from a braced-init-list. A braced-init-list is being bound to auto.
template <typename T>
void PrintTypeName(std::initializer_list<T>&& t)
{
...
}
PrintTypeName({1,2,3});
This is another of the specific situations where a braced-init-list is automatically converted to std::initializer_list. A braced-init-list is being used as a function call argument and the corresponding function accepts a std::initializer_list parameter.
template<typename T>
void PrintTypeName(T&& t)
{
...
}
PrintTypeName({1,2,3});
This is not one of the situations where a braced-init-list will automatically be converted to a std::initializer_list. This PrintTypeName does not specifically accept a std::initializer_list.

Ensuring template argument type matches that of its variadic constructor

I would like to have a class like this:
template<typename T>
struct Foo {
T* data_;
template<typename... Ts, std::enable_if<std::is_same<T,Ts>...>...>
explicit Foo(Ts...ts) : data_{ ts... } {}
};
However; something with the syntax is wrong, and I'm not sure if you can set parameters into a pointer directly like this upon initialization.
What I would like for this to do is simply this:
Foo<int> f1{ 1, 3, 5, 7 }; // Or
// Foo<int> f1( 1, 3, 5 7 );
// f1.data_[0] = 1
// f1.data_[1] = 3
// f1.data_[2] = 5
// f1.data_[3] = 7
// f1.data_[4] = ... not our memory either garbage or undefined...
Foo<float> f2{ 3.5f, 7.2f, 9.8f }; // Or
// Foo<float> f2( 3.5f, 7.2f, 9.8f );
// f2.data_[0] = 3.5
// f2.data_[1] = 7.2
// f2.data_[2] = 9.8
// f2.data_[3] = ... not our memory
I would also like to have the constructor check to make sure that each and every parameter that is passed into the constructor is of type <T>; simply put for each Ts it must be a T.
I might be overthinking this but for the life of me I can not get this or something similar to compile. I don't know if it's within enable_if, is_same or through the class's initializer list and trying to store the contents into a pointer. I don't know if I should use an array of T instead but the array's size won't be known until the arguments are passed into the constructor. I'm also trying to do this without using a basic container such as std::vector; it's more for self education than practical source code. I just want to see how this could be done with raw pointers.
Edit
I've changed my class to something like this:
template<typename T>
struct Foo {
T* data_;
template<typename... Ts, std::enable_if_t<std::is_same<T, Ts...>::value>* = nullptr>
explicit Foo( const Ts&&... ts ) : data_{ std::move(ts)... } {}
};
And when trying to use it:
int a = 1, b = 3, c = 5, d = 7;
Foo<int> f1( a, b, c, d );
Foo<int> f2{ a, b, c, d };
I'm a little closer with this iteration; but they both give different compiler errors.
The first being: C2661: "No overloaded function takes 4 arguments"
And the second: C2440: "initializing, cannot convert from initializer list to Container, no constructor could take the source type, or constructor overload resolution was ambiguous."
Why not simply use a std::initialize_list:?
#include <iostream>
#include <type_traits>
#include <vector>
template <class T>
struct Foo
{
std::vector<T> data_;
explicit Foo(std::initializer_list<T> data) : data_(data)
{
std::cout << "1";
};
template <typename... Ts,
typename ENABLE=std::enable_if_t<(std::is_same_v<T,Ts> && ...)> >
explicit Foo(Ts... ts) : Foo(std::initializer_list<T>{ts...})
{
std::cout << "2";
}
};
int main()
{
Foo<int> f1{1, 3, 5, 7}; // prints 1
Foo<int> f2(1, 3, 5, 7); // prints 1 then 2
return 0;
}
If some Ts are different from T you will get a compile-time error.
With
gcc -std=c++17 prog.cpp
you get:
Foo<int> f1{1, 3, 5., 7};
error: narrowing conversion of ‘5.0e+0’ from ‘double’ to ‘int’ inside
{ } [-Wnarrowing] Foo f1{1, 3, 5., 7};
^
and
Foo<int> f2(1, 3, 5., 7);
you get
error: no matching function for call to ‘Foo::Foo(int, int,
double, int)’ Foo f2(1, 3, 5., 7);
^ note: candidate: ‘template Foo::Foo(Ts ...)’ explicit Foo(Ts... ts) :
Foo(std::initializer_list{ts...})
...
Update: if you really want to use something like raw pointer, here is a complete working example:
#include <iostream>
#include <memory>
#include <type_traits>
#include <vector>
template <class T>
struct Foo
{
size_t n_;
std::unique_ptr<T[]> data_;
explicit Foo(std::initializer_list<T> data) : n_(data.size()), data_(new T[n_])
{
std::copy(data.begin(), data.end(), data_.get());
std::cout << "1";
};
template <typename... Ts, typename ENABLE = std::enable_if_t<(std::is_same_v<T, Ts> && ...)> >
explicit Foo(Ts... ts) : Foo(std::initializer_list<T>{ts...})
{
std::cout << "2";
}
friend std::ostream& operator<<(std::ostream& out, const Foo<T>& toPrint)
{
for (size_t i = 0; i < toPrint.n_; i++)
std::cout << "\n" << toPrint.data_[i];
return out;
}
};
int main()
{
Foo<int> f1{1, 3, 5, 7}; // prints 1
Foo<int> f2(1, 3, 5, 7); // prints 1,2
std::cout << f1;
std::cout << f2;
return 0;
}
I let you replace unique_ptr by a raw pointer with all the extra work: delete[] etc...
std::is_same only compares two types, and you can't use pack expansions to declare multiple template parameters. That means you'll need to pull all of your std::is_same checks out into another check:
template <typename T, typename... Ts>
struct all_same : std::bool_constant<(std::is_same<T, Ts>::value && ...)> {};
template <typename T>
struct Foo
{
std::vector<T> data_;
template <typename... Ts, std::enable_if_t<all_same<T, std::decay_t<Ts>...>::value>* = nullptr>
Foo(Ts&&... ts)
: data_{std::forward<Ts>(ts)...}
{
}
};
Live Demo
You also need to allocate memory for your data_ array. Here I've used std::vector to take care of that allocation for me, but you could use new[] and delete[] to manage it yourself if you really want to.
enable_if and is_same won't store anything anywhere, they are only compile-time constructs and do not yield to any code in the binary executable.
Regardless of the syntax, what your code is essentially doing is trying to take the address of a constructor argument (which is a temporary). This will be a dangling pointer as soon as the constructor exits.
Either Foo owns the memory area and must allocate in constructor and delete in destructor (if any doubt: use std::vector!), or it aliases some external memory, and must receive a pointer to that memory.
Now regarding syntax:
std::is_same is a template that provides a value boolean constant and is to be used like so: std::is_same<T1, T2>::value. Alternatively you can use std::is_same_v<T1, T2>.
std::enable_if provides a type type member, only if the constant expression (1st template parameter) is true. Use it like std::enable_if<expr, T>::type. If expr is true, type is a typedef to T. Otherwise it is not defined and yields a substitution failure. Alternatively you can use std::enable_if_t<expr, T>
You can have a look here for a similar approach of yours.
But you can also simplify all this by using a member vector. In that case, the vector constructor ensures that all arguments have compatible types. Here is a complete example:
#include <string>
#include <vector>
#include <iostream>
#include <type_traits>
using namespace std;
template<typename T>
struct Foo {
vector<T> data_;
template<typename ...Ts>
explicit Foo(Ts... ts) : data_{ ts... } {}
void print() {
for (const auto &v : data_) {
cout << v << " ";
}
cout << endl;
}
};
int main() {
Foo<int> ints { 1, 2, 3, 4, 5 };
Foo<string> strings { "a", "b", "c", "d", "e"};
// Foo<string> incorrect { "a", 2, "c", 4, "e"};
ints.print();
strings.print();
// incorrect.print();
return 0;
}
The most idiomatic way to do this in C++17 is using std::cunjunction_v. It lazily evaluates subsequent values and allows you to avoid fold expressions. Also messages generated by the compiler are the same for both of casese you mentioned in edited piece of code.
Also, passing data by const-rvalue-ref makes no sense, since it is impossible to move data from const objects. Additionaly you were moving pack of arguments to a pointer. I didn't know what to do about it so just removed it.
Additionaly, take a look at the std::decay_t - without it it would not work, as sometimes Ts is deduced not as int but as const int & or int &. This leads to std::is_same_v being false.
The code below compiles fine at godbolt:
template<typename T>
struct Foo {
T* data_;
template<
typename... Ts,
std::enable_if_t<
std::conjunction_v<
std::is_same<T, std::decay_t<Ts>>...
>
> * = nullptr
>
explicit Foo( Ts&&... ts ) : data_{ } {}
};

C++17 almost uniform initialization

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)...};
}
}

How to pass variadic amount of `std::pair` with different 2nd types to a function

Sorry for the inability to explain the primary Q in the title itself due to complexity of the problem.
Need to pass various types of std::pairs to a method like below:
foo({1, 1} , {2, 2.2}, {3, "3"}); // 'first' is always `int`
However, I couldn't figure out a syntax for How to define foo() using variadic templates?
This is more of a cosmetic change, where the intention is to avoid the boiler plate code. Hence below suggestion is ruled out:
template<typename... Args>
void foo (Args&&... args) { ... }
template<typename T> using pair = std::pair<int, T>;
foo(pair<int>{1, 1} , pair<double>{2, 2.2}, pair<std::string>{3, "3"});
For anyone who is interested, what I am going to do with various pairs. An overloaded function will be invoked on all the args... (using array trick) and all the second values will be converted to a std::string. For those who don't know the famous array trick:
const string array[] = { MyFunc(std::forward<Pairs>(pairs)) ... };
Similar (but not duplicate) question: Passing multiple std::pair to variadic template constructor using { }, { }
If your intention is call foo() in this way
foo({1, 1} , {2, 2.2}, {3, "3"});
using curly braces with values instead of explicit std::pair<int, int>{1,1}, std::pair<int, double>{1, 2.2}, .... ... I don't think it's possible.
If you can give up the curly braces, so calling foo() in this way,
foo(1, 1 , 2, 2.2, 3, "3");
and constructing pair inside foo(), you can do something like
#include <string>
#include <utility>
void foo ()
{
// do nothing ?
}
template <typename T2, typename ... Ts>
void foo (int v1, const T2 & v2, const Ts & ... vs)
{
std::pair<int, T2> p { v1, v2 };
// do something with p
foo(vs...);
}
int main()
{
foo(1, 1, 1, 2.2, 1, std::string("3"));
return 0;
}
But, frankly, I didn't like this solution, because isn't clear wich pairs are calling foo(), so I think it's a better way use rahnema1's solution calling foo() using make_pair()
foo(std::make_pair(1, 1), std::make_pair(1, 2.2),
std::make_pair(1, std::string("3")));
You can simply use this signature:
template<typename... Args>
void foo (std::pair<int, Args> ...args) { /*...*/}
or
template <typename ...Args> using pair = std::pair<int, Args...>;
Edit: As mentioned the question is about constructing std::pair withot providing template arguments and to convert second part of pair to string.
with overloading the () operator can we write:
#include <iostream>
#include <vector>
#include <sstream>
struct Function{
template <typename T>
Function& operator()(int a, T b){
ss << b;
list.push_back(ss.str());
ss.str("");
return *this;
}
std::vector<std::string> get_string(){
return list;
}
std::vector<std::string> list;
std::stringstream ss;
};
int main(){
Function foo;
for(auto& s: foo(1, 3.0)(2,"foo")(3, 5.0f).get_string())
{
std::cout << s << std::endl;
}
}
Closest I was able to get was a variadic template of pairs, in which the 2nd option is a template type, but the same type for every pair.
template<typename DataType, template<class, class> typename ...Pair>
void foo(const std::pair<int, DataType>& nextParam, const Pair<int, DataType>& ...remainingParams);
void foo({1, "a"}, {2, "b"}, {3, "c"}); // works
void foo({1, "a"}, {2, 3.14}, {3, 'A'}); // doesn't work
Perhaps there is a way to get the template parameter to be variadic. That said I'm a bit doubtful of that, you essentially need to be able to specify a single element of a pack or rather to use a single parameter from a pack without expanding it, but also acknowledging that it is in fact a pack.