Have a situation where a templated class constains a templated member and said member object must be constructed using the proper constructor (which depends on a class template). Simplified example:
#include <stdexcept>
#include <memory>
#include <iostream>
#include <vector>
template<int N=0>
struct A
{
const int n_;
A(int n, double)
: n_{n}
{
static_assert(N <= 0);
}
A(double)
: n_{N}
{
static_assert(N > 0);
}
};
template<int N=0>
struct B
{
const std::vector<int> m_;
// Solution with unique_ptr class member to an A<N> object
std::unique_ptr<A<N>> a_uptr_;
B(const std::vector<int>& m, double x)
: m_{m},
a_uptr_{N > 0 ? std::make_unique<A<N>>(x) : std::make_unique<A<N>>(m.size(), x)}
{}
// Solution with class member A<N> object
// A<N> a_;
// B(const std::vector<int>& m, double x)
// : m_{m},
// a_{ <????????????????????> }
// {}
};
int main()
{
std::vector<int> m{1,2,3};
B b1(m, 0);
B<2> b2(m, 0);
std::cout << b1.a_uptr_->n_ << ", " << b2.a_uptr_->n_ << std::endl;
}
Q1: Is there a way to just have an A<N>
class member (instead of a unique ptr) and have it call the correct constructor? Using an std::integer_sequence
perhaps???
(Q2: Am I overthinking this, i.e. should I really be worried about performance of having to put the A<N>
obj on the heap?)
Assuming the question is only about B
, that A
is supposed to not change...
Then what you face is what I would call "complicated construction of a member in the initializer list" and the soltuion for this is generally a static method:
#include <stdexcept>
#include <iostream>
#include <vector>
template<int N=0>
struct A {
const int n_;
A(int n, double) : n_{n} {
if constexpr (N > 0) throw std::runtime_error("wrong constructor for N > 0");
}
A(double) : n_{N} {
if constexpr (N <= 0) throw std::runtime_error("wrong constructor for N <= 0");
}
};
template<int N=0>
struct B {
const std::vector<int> m_;
A<N> a_uptr_;
B(const std::vector<int>& m, double x)
: m_{m},
a_uptr_{make_AN(m,x)}
{}
private:
static A<N> make_AN(const std::vector<int>& m,double x) {
if constexpr (N>0) return x;
else return {m.size(),x};
}
};
int main() {
std::vector<int> m{1,2,3};
B b1(m, 0);
B<2> b2(m, 0);
std::cout << b1.a_uptr_.n_ << ", " << b2.a_uptr_.n_ << std::endl;
}
Or as mentioned in comments (Some programmer dude and n.m.), simply a_{N > 0 ? A<N>{m} : A<N>{m, x}}
. Perhaps you are afraid of copies which are elided anyhow.
However, you should consider to rewrite A
such that it is a compile time error to call A<N>::A(int,double)
with N > 0
and A<N>::A(double)
with N <= 0
.