Initializer list issue in constructor - c++

I have a hard time understanding how std::initializer_list works. I checked other questions, but found nothing relevant (or maybe I didn't see it?).
Say I have this:
template<typename T>
struct Point
{
T x,y;
};
template<typename T>
struct A
{
std::vector<Point<T>> v;
};
Then I can build with:
int main()
{
A<int> a{ std::vector<Point<int>> { {4,4}, {5,5},{6,6} } };
}
But I'd like to make thing simpler, so I can write:
int main()
{
A<int> a( { {4,4}, {5,5},{6,6} } );
}
I tried:
template<typename T>
struct A
{
std::vector<Point<T>> v;
template<typename U>
A( const std::initializer_list<Point<U>>& il ) : v{il}
{}
};
But this fails, see live demo.
How can I write a constructor allowing this? Is this even possible?

Your constructors should not be template, your class is already. {..} has no type and can be deduced to very few types.
Once removed, following works:
template<typename T>
struct A
{
std::vector<Point<T>> v;
A( const std::vector<Point<T>>& in ) : v(in) {}
A( const std::initializer_list<Point<T>>& il ): v{il}
{}
};
int main()
{
std::vector<Point<int>> v{ {4,4}, {5,5},{6,6} };
A<int> a1( std::vector<Point<int>> { {4,4}, {5,5},{6,6} } ); // this is fine
assert( a1.v.size() == 3 );
A<int> a2{{4,4}, {5,5}, {6,6} };
assert( a2.v.size() == 3 );
A<int> a3({{4,4}, {5,5}, {6,6} });
}
Demo

With C++20 there is a feature called parenthesized initialization of aggregates which allows your given syntax to be well-formed.
template<typename T>
struct Point
{
T x, y;
};
template<typename T>
struct A
{
std::vector<Point<T>> v;
};
int main()
{
//--------------v------------------------v---> parenthesis ok with c++20, replace () with {} for pre-c++20
A<int> a( { {4, 4}, {5, 5}, {6, 6} } );
}
Working demo
This means that if you're working with pre-c++20, you can replace the parenthesis () with braces {}. demo c++14

First of all, your desired syntax almost works (without adding a constructor to A), just replace (...) with {...}.
int main()
{
A<int> a{{ {4,4}, {5,5},{6,6} }};
}
Second of all, if you still want to write a constructor for A don't use std::initializer_list for it! std::vector already does it, so you don't have to. Just pass the vector:
template<typename T>
struct A
{
std::vector<Point<T>> v;
A(std::vector<Point<T>> v) : v(std::move(v)) {}
};
int main()
{
A<int> a( { {4,4}, {5,5},{6,6} } );
}

Related

test class type without specifying template argument in c++20 [duplicate]

This question already has answers here:
How can I define a concept that is satisfied by an arbitrary std::vector?
(3 answers)
Closed last year.
I would like to select instance by type while iterating via std::apply in a std::tuple.
Since I have multiple times the same class in my tuple I can not just use std::get
Is there a way to express that I want all the variant of A instead of writting
A< ... >
#include <iostream>
#include <tuple>
template <typename T>
struct A
{
public:
T val;
A(T t) : val(t)
{};
};
using MyType = std::tuple< A<double> , A<double> , A<int>, bool>;
template<typename T>
void logic(T& c)
{
if constexpr ( std::is_same<T, A< ... > >::value)
std::cout << c.val << std::endl;
}
void printOnlyA(MyType& v)
{
std::apply([&](auto &&...c) { ( logic(c), ...); }, v);
}
int main() {
A<double> a{3.5};
A<double> b{5.5};
A<int> c{2};
MyType dd{std::make_tuple(a,b,c , false)};
printOnlyA(dd);
return 0;
}
I m using c++ 20, and would like to avoid the old c++11 approach to std::false_type and std::true_type. I can not figure out the way to do it with concept
Thanks in advance
You can use partial template specialization. Since partial specialization for functions is not a part of the language a dedicated class template is required:
#include <iostream>
#include <tuple>
template <typename T>
struct A
{
public:
T val;
A(T t) : val(t)
{};
};
using MyType = std::tuple< A<double> , A<double> , A<int>, bool>;
template<typename T>
struct logic_impl
{
static void exec([[maybe_unused]] T & c)
{
// nothing
}
};
template<typename T>
struct logic_impl<A<T>>
{
static void exec(A<T> & c)
{
std::cout << c.val << std::endl;
}
};
template<typename T>
void logic(T& c)
{
logic_impl<T>::exec(c);
}
void printOnlyA(MyType& v)
{
std::apply([&](auto &&... c) { (logic(c), ...); }, v);
}
int main() {
A<double> a{3.5};
A<double> b{5.5};
A<int> c{2};
MyType dd{std::make_tuple(a,b,c , false)};
printOnlyA(dd);
return 0;
}
online compiler

How to partially specialize std::unordered_map as a member of my templated class?

I can't seem to understand why this does not work:
#include <unordered_map>
#include <vector>
template<typename T>
struct Bar {
Bar(const T &t) : x{t} {}
T x;
};
template<typename T>
struct Foo {
std::unordered_map<T, Bar<T>> u;
Foo(const std::vector<T> &v) {
for (const T &i : v)
u[i] = Bar(i);
}
};
int main() {
Foo<int> f({1, 2, 3});
}
Try it here
What I want is to have an instance of Foo that contains an unordered_map that maps objects of type T to objects of type Bar. The error message is unfortunately not as helpful as I wish:
error: no matching function for call to 'Bar<int>::Bar()'
What is happening here? How do I solve this problem?
As #songyuanyao has very cleverly noticed, the problem was that std::unordered_map::operator[] returns a reference to the mapped type, which requires a constructor that takes no arguments. Using std::unordered_map::insert solves this without requiring the introduction of such constructor in bar:
#include <unordered_map>
#include <vector>
template<typename T>
struct Bar {
Bar(const T &t) : x{t} {}
T x;
};
template<typename T>
struct Foo {
std::unordered_map<T, Bar<T>> u;
Foo(const std::vector<T> &v) {
for (const T &i : v)
u.insert({i, Bar<T>(i)});
}
};
int main() {
Foo<int> f({1, 2, 3});
}
Try it here

Allowing both pre-computed and computed-on-the-fly results

Consider:
template <typename T>
struct C {
std::vector<int> f(const T &t) const {
return t.f();
}
};
T::f must compute the required vector. However, some T's pre-compute the vector and we would like to avoid creating a copy in such a case. Here is my attempt:
struct A {
std::vector<int> f() const {
std::vector<int> res{10, 20, 30}; // computing the vector.
return res;
}
};
struct B {
const std::vector<int> &f() const {
return v_; // returning reference to pre-computed vector.
}
private:
std::vector<int> v_{1, 2, 3};
};
template <typename T>
struct C {
const std::vector<int> &f(const T &t) const {
return t.f();
}
};
int main() {
using T = B; // For A, we get an error about returning reference to a local.
C<T> c;
T t;
c.f(t);
return 0;
}
As the comment in main indicates, for T=A, the above code is in error as it is returning a reference to the local variable. How can I accommodate for both T=A and T=B, such that the pre-computed vector B::v_ does not get copied?
Make C::f return the exact same type as T::f, by using decltype:
template <typename T>
struct C {
auto f(const T &t) const -> decltype(t.f()) {
return t.f();
}
};
This will return by value when T = A, and by const& when T = B.
wandbox example
Your attempt isn't testing what it should. For the pre-computed results to be useful, they need to be persistent. One class needs to do both the computation and the storage.
For example,
struct A {
std::vector<int> const & f() const {
if ( v_.empty() ) {
v_ = {10, 20, 30}; // computing the vector.
}
return v_;
}
private:
mutable std::vector<int> v_;
};
Another architecture would be to store all the results in one std::map< X, std::vector > (or unordered_map), if there's some type X defining the domain of the function.

Passing a default value list to a template class

Not sure if this can be done using templates but I want to give it a try.
I have a template class which takes any struct, stores it and returns it. Additionally, I want an interface that resets the struct's data whenever requested.
#define MYDEFAULT {1,2,3}
template < typename ITEM, ITEM Default>
class myClass{
public:
myClass(ITEM item) : _item(item) {}
const ITEM* get(){
return &_item;
}
void reset(){
_item = Default;
}
ITEM _item;
};
// Set to default when instantiated
myClass<myStruct, MYDEFAULT> ABC(MYDEFAULT);
Of course that's not working at all, but what I want to achieve is the replacement of Default in reset(). I mean it would work if _item would be of type int.
How can this be realized?
EDIT: I want something like this:
template <typename Y, Y T>
class myclass {
public:
void reset() {
xxx = T;
}
Y xxx{10};
};
void test()
{
myclass<int, 5> _myclass;
}
Initially xxx is 10 and after invoking reset it is 5. This works, so it seems it is not possible for POD or class types?
EDIT2: It seems it is all about non-type template-arguments. https://stackoverflow.com/a/2183121/221226
So there is no way around traits when using structs.
As a viable solution, you can use a trait class as shown in the following working example:
#include<cassert>
struct S {
int i;
};
template<typename T>
struct Traits {
static constexpr auto def() { return T{}; }
};
template<>
struct Traits<S> {
static constexpr auto def() { return S{42}; }
};
template <typename ITEM>
class myClass {
public:
myClass(): _item(Traits<ITEM>::def()) {}
myClass(ITEM item): _item(item) {}
const ITEM* get() {
return &_item;
}
void reset() {
_item = Traits<ITEM>::def();
}
ITEM _item;
};
int main() {
myClass<S> ABC{};
myClass<int> is;
assert((ABC.get()->i == 42));
assert((*is.get() == 0));
}
The basic trait uses the default constructor of the type ITEM.
You can then specialize it whenever you want a different defaulted value for a specific class.
The same can be accomplished even with a factory function as:
template<typename T>
constexpr auto def() { return T{}; }
template<>
constexpr auto def<S>() { return S{42}; }
Anyway, traits can easily provide more types and functions all at once.
You can maybe simulate it using a data structure with a data member of type std::array.
A minimal, working example follows:
#include<cstddef>
#include<array>
#include<cassert>
template<typename T, T... I>
struct S {
S(): arr{ I... } {}
S(const T (&val)[sizeof...(I)]) {
for(std::size_t i = 0; i < sizeof...(I); ++i) {
arr[i] = val[i];
}
}
const T * get() {
return arr.data();
}
void reset() {
arr = { I... };
}
private:
std::array<T, sizeof...(I)> arr;
};
int main() {
S<int, 1, 3, 5> s{{ 0, 1, 2 }};
assert(s.get()[1] == 1);
s.reset();
assert(s.get()[1] == 3);
}
I'm not sure I got exactly what you are asking for, but the interface in the example is close to the one in the question and the implementation details should not affect the users of your class.

initialize stl valarray in one row

Hello I wannted to build a helper class to initialize a stl valarray. What I would like is to do the following:
std::valarray<float> vec(3);
vlist_of<float>(vec)(2)(3)(5);
So I can just initialize the vectors at runtime using just one row command statement.
For doing the following I have tryed the following structure:
template <typename T>
struct vlist_of {
std::valarray<T>& data;
int i;
vlist_of(std::valarray<T>& _data):data(_data),i(0) {
(*this)(data);
}
vlist_of& operator()(std::valarray<T>& data){return *this;}
vlist_of& operator()(const T& t) {
data [i]=t;
i++;
return *this;
}
};
this structure works if I do the following :
vlist_of<float> tmp(vec);tmp(2)(3)(4);
Is it possible what I am asking ?
Yes. Make vlist_of a factory function:
template <typename T>
vlist_builder<T> vlist_of(std::valarray<T>& data)
{
return vlist_builder<T>(data);
}
Now it works http://liveworkspace.org/code/48aszl$0.
I'd personally prefer uniform initialization:
/*const*/ std::valarray<float> prefer { 2, 3, 5 };
See full sample:
#include <valarray>
#include <vector>
#include <iostream>
template <typename T>
struct vlist_builder
{
std::valarray<T>& data;
int i;
vlist_builder(std::valarray<T>& _data):data(_data),i(0) { }
vlist_builder& operator()(const T& t)
{
data[i++]=t;
return *this;
}
};
template <typename T>
vlist_builder<T> vlist_of(std::valarray<T>& data)
{
return vlist_builder<T>(data);
}
int main()
{
std::valarray<float> vec(3);
vlist_of<float>(vec)(2)(3)(5);
for(auto f : vec)
std::cout << f << "\n";
// prefer uniform initialization:
const std::valarray<float> prefer { 2, 3, 5 };
}