I am trying to use templates to represent simple polynomials like x^2 + 3x + 5. My idea is to represent them as sum of terms with each term having a coefficient and a power so e.g. x^2 has coeff=1 and power=2. I also want to be able to evaluate the polynomials for some x (they only have 1 unknown but in many places). So far I have:
struct PolyEnd{
double eval(double x){
return 0;
}
};
template <int coeff, int power, class Tail> struct Poly {
typedef Tail tt;
double eval(double x){
double curr = coeff * std::pow(x, power);
return curr; // has to call eval(x) on rest of the terms which are in the tail and return the sum with "curr"
}
};
int main()
{
double x = 2;
Poly<1,1,Poly<1,1,PolyEnd>> poly;
std::cout << poly.eval(x) << std::endl;
return 0;
}
However, I am stuck. Is what I am trying even possible? If so how can I make the recursive eval() calls work?
Yes, you can do that, you just need to call eval on the tail and since all the classes are state-less, you can just create an instance to call the member function on, on the spot:
struct PolyEnd{
double eval(double x){
return 0;
}
};
template <int coeff, int power, class Tail> struct Poly {
typedef Tail tt;
double eval(double x){
double curr = coeff * std::pow(x, power);
return curr + Tail{}.eval(x);
}
};
int main()
{
double x = 2;
Poly<1,1,Poly<1,1,PolyEnd>> poly;
std::cout << poly.eval(x) << std::endl;
return 0;
}
or if you make eval static, then you can call Tail::eval(x) directly.
I guess that you are experimenting with metaprogramming. Your question also made me excited, because i am also newbie in metaprogramming and I want to practise. #walnut's answer already accepted but there is no harm to share another implementation. I used some basic metaprogramming techniques.
I hope it will help you.
#include <cmath>
#include <iostream>
#include <string>
template<int Value>
struct coeff
{ };
template<int Value>
struct power
{ };
template<typename Coefficient, typename Power>
struct term;
template<int Coefficient , int Power>
struct term< coeff<Coefficient> , power<Power> >
{
inline double eval( double x ) const noexcept {
return Coefficient * std::pow( x , Power );
}
};
template<int Value>
using constant = term< coeff<Value> , power<1> >;
template<int Value>
using exponential = term< coeff<1> , power<Value> >;
template<typename... T>
struct polynomial
{
static_assert( sizeof...(T) == 0, "A polynomial can only be expressed in 'term's.");
[[nodiscard]] constexpr double eval( double ) const noexcept {
return 0;
}
[[nodiscard]] std::string to_string() const noexcept {
return std::string{};
}
};
template<int Coefficient, int Power, typename... Tail>
struct polynomial<term< coeff<Coefficient> , power<Power> >, Tail...>
: polynomial<Tail...>
{
[[nodiscard]] constexpr double eval( double x ) const noexcept {
return m_t.eval( x ) + polynomial<Tail...>::eval( x );
}
[[nodiscard]] std::string to_string(){
using namespace std;
using namespace std::string_literals;
return "("s + std::to_string( Coefficient ) +
string { "x^" } +
std::to_string( Power ) + ( sizeof...(Tail) == 0 ? ")" : ") + " ) +
polynomial<Tail...>::to_string();
}
private:
term< coeff<Coefficient> , power<Power> > m_t;
};
int main()
{
auto p1 = polynomial<term< coeff<1> , power<2> > ,
term< coeff<2> , power<4> > ,
term< coeff<2> , power<3> > ,
constant<3> ,
exponential<2> >{};
std::cout << "Polynomial is : " << p1.to_string() << std::endl;
std::cout << "f(2) : " << p1.eval( 2 ) << std::endl;
std::cout << "f(3) : " << p1.eval( 3 ) << std::endl;
return 0;
}
run online
Polynomial coefficients can be stored in std::array or std::vector(in case you define polynomial degree in runtime).
Then extend functionality with eval function.
template <unsigned N>
class Poly : public std::array<double, N> {
public:
template <typename... E>
Poly(E &&... e) : std::array<double, N>{{std::forward<E>(e)...}} {}
double eval(double x) {
double result = 0;
double exp = 1.;
for (auto it = this->rbegin(); it != this->rend(); ++it) {
result += exp * (*it);
exp *= x;
}
return result;
}
};
usage
double result = Poly<3>{3., 2., 1.}.eval(17);
Related
I have the following two objects. Im wondering if there is a way to have Pixel as a base class of PixelBGR so that any operator (+,-,*,/, [], etc.) could be used without redefining them ?
template<class T, std::size_t N>
struct Pixel
{
T ch[N];
inline T& operator[](const int x)
{
return ch[x];
}
};
template<class T>
struct PixelBGR
{
union
{
struct
{
T b;
T g;
T r;
};
T ch[3];
};
inline T& operator[](const int x)
{
return ch[x];
}
};
EDIT: As suggested by πάντα ῥεῖ, here more details about what Im trying to do.
Im trying to have a generic class Pixel, which will be template to handle any type or size.
The usual are 1,2,3,4,8 or 16. The class with defines some operator such as +,-,*, etc.
Since most of the time, the Pixel<T,3> is a BGR pixel, I would like to define rapid access to r,g and b to avoid confusion, but still store it as BGR.
But the derived class should also provide the Operator which will be generic based on N.
EDIT2: By reading the comment of SergeyA, I forgot to say that the struct Pixel must not change size.
So I think balki answer is the best, by using member function. I was trying to make it with variables to avoid too much char ie: adding the (), but it seems to be too complicated for nothing. I still investigating CRTP, but I dont get it well, Im reading on that.
Answering the question as asked, this should give OP reuse of the operators without any undefined behavior:
#include <cstddef>
template<class T, std::size_t N>
struct Pixel
{
T ch[N];
inline T& operator[](const int x)
{
return ch[x];
}
Pixel& operator+= (const Pixel& ) { return *this;}
};
template<class T, std::size_t N>
Pixel<T, N> operator+ (const Pixel<T, N>& l, const Pixel<T, N>& r);
template<class T>
struct BgrPixel : Pixel<T, 3> {
using base = Pixel<T, 3>;
using base::base;
BgrPixel(const base& b) : base(b) { };
T& b = base::ch[0];
T& g = base::ch[1];
T& r = base::ch[2];
};
BgrPixel<int> a, b;
BgrPixel<int> c = a + b;
Alterinative would be to have b(), g() and r() as a member functions, but this would require you to access them as functions. You would also need const and non-const versions of them.
The benefits, however, would be that the size of the struct will not be increased and copy assignment would work naturally (which, in turn, could be solved by providing custom copy assignment).
The Curiously Recurring Template Pattern (CRTP) would work well in this case. In the CRTP the Derived class is used as a template argument to the Base class. Chapter 16.3 The Curiously Recurring Template Pattern (CRTP), from the C++ Templates - The Complete Guide, by David Vandevoorde and Nicolai M. Josuttis, explains things in more detail.
From the comments below, the usage of a union{struct{...}...} causes undefined behaviour (UB), but there have been some contradicting opinions upon this. As far as I'm aware, it is a gnu extension and supported by almost every compiler. glm for example uses union-structs quite very often.
As an alternative approach, you can use aliases (references) for the r,g,b variables.
#include <iostream>
template<typename T, std::size_t N, template<typename,std::size_t> class B >
struct Pixel
{
B<T,N> *crtp = static_cast<B<T,N>*>(this);
T& operator[](std::size_t x)
{
return crtp->ch[x];
}
Pixel& operator = (const Pixel &t)
{
crtp->ch[0] = t.crtp->ch[0];
crtp->ch[1] = t.crtp->ch[1];
crtp->ch[2] = t.crtp->ch[2];
return *crtp;
}
B<T,N> operator + (const B<T,N> &t)
{
B<T,N> tmp;
tmp[0] = crtp->ch[0] + t.crtp->ch[0];
tmp[1] = crtp->ch[1] + t.crtp->ch[1];
tmp[2] = crtp->ch[2] + t.crtp->ch[2];
return tmp;
}
B<T,N> operator - (const B<T,N> &t)
{
B<T,N> tmp;
tmp[0] = crtp->ch[0] - t.crtp->ch[0];
tmp[1] = crtp->ch[1] - t.crtp->ch[1];
tmp[2] = crtp->ch[2] - t.crtp->ch[2];
return tmp;
}
};
template<typename T, std::size_t N=3>
struct PixelBGR : Pixel<T, N, PixelBGR>
{
T ch[3];
T &r;
T &g;
T &b;
PixelBGR() : ch{},r(ch[0]),g(ch[1]),b(ch[2])
{}
PixelBGR& operator = (const PixelBGR &p)
{
ch[0] = p.ch[0];
ch[1] = p.ch[1];
ch[2] = p.ch[2];
return *this;
}
};
int main()
{
PixelBGR<int> p;
p.r = 25;
p.g = 14;
p.b = 58;
std::cout<< p[0] <<" , "<<p[1]<<" , "<<p[2] <<std::endl;
PixelBGR<int> q;
q = p;
std::cout<< q[0] <<" , "<<q[1]<<" , "<<q[2] <<std::endl;
PixelBGR<int> res1;
res1 = q + p;
std::cout<< res1.r <<" , "<<res1.g<<" , "<<res1.b <<std::endl;
PixelBGR<int> res2;
res2 = q - p;
std::cout<< res2.r <<" , "<<res2.g<<" , "<<res2.b <<std::endl;
}
Result:
25 , 14 , 58
25 , 14 , 58
50 , 28 , 116
0 , 0 , 0
Example using references: https://rextester.com/AZWG4319
Example using union-struct: https://rextester.com/EACC87146
First thanks to all of you for advise, and special thanks to #Constantinos Glynos, #balki and #SergeyA for the example they provide, those help me to achieve a solution that match my need.
I implemented the BGR and BGRA to show that the N works fine, now I just need to implement all the operator, and functions that I require.
Please feel free to edit, or tell me if there is something wrong with this.
Pixel.h
#include <cstdio> // std::size_t
#include <iostream> // std::cout
template<typename T, std::size_t N, template<typename, std::size_t> class B >
struct Pixel
{
T ch[N];
// ==============================================================
// Overload the accessor (so .ch[0] == direct access with [0].
T& operator[](std::size_t x){ return ch[x]; }
// ==============================================================
// Copy-assignement
Pixel& operator=( const Pixel &t )
{
for ( int i = 0; i < N; i++ )
ch[i] = t.ch[i];
return *this;
}
// ==============================================================
// Operator
B<T, N> operator+( const B<T, N> &t )
{
B<T, N> tmp;
for ( int i = 0; i < N; i++ )
tmp[i] = ch[i] + t.ch[i];
return tmp;
}
B<T, N> operator-( const B<T, N> &t )
{
B<T, N> tmp;
for ( int i = 0; i < N; i++ )
tmp[i] = ch[i] - t.ch[i];
return tmp;
}
template<typename T, std::size_t N, template<typename, std::size_t> class B >
friend std::ostream& operator<<( std::ostream& os, const Pixel &t );
};
// To print the vector
template<typename T, std::size_t N, template<typename, std::size_t> class B >
std::ostream& operator<<( std::ostream& os, const B<T, N> &t )
{
os << "Pixel: (" << t.ch[0];
for ( int i = 1; i < N; i++ )
os << ", " << t.ch[i];
os << ")";
return os;
}
template<typename T, std::size_t N = 3>
struct BGR : Pixel<T, N, BGR>
{
T& b() { return ch[0]; }
T& g() { return ch[1]; }
T& r() { return ch[2]; }
};
template<typename T, std::size_t N = 4>
struct BGRA : Pixel<T, N, BGRA>
{
T& b() { return ch[0]; }
T& g() { return ch[1]; }
T& r() { return ch[2]; }
T& a() { return ch[3]; }
};
Main.cpp
int main() {
std::cout << "Sizeof a float BGR: " << sizeof(BGR<float>) << std::endl;
std::cout << "Sizeof a float BGRA: " << sizeof(BGRA<float>) << std::endl;
BGR<int> p;
p.r() = 25;
p.g() = 14;
p.b() = 58;
std::cout << p << std::endl;
std::cout << p[0] << " , " << p[1] << " , " << p[2] << std::endl;
std::cout << p.b() << " , " << p.g() << " , " << p.r() << std::endl;
BGR<int> q;
q = p;
std::cout << q[0] << " , " << q[1] << " , " << q[2] << std::endl;
BGR<int> res1;
res1 = q + p;
std::cout << res1.r() << " , " << res1.g() << " , " << res1.b() << std::endl;
BGR<int> res2;
res2 = q - p;
std::cout << res2.r() << " , " << res2.g() << " , " << res2.b() << std::endl;
BGRA<float> a;
a.r() = 255.0f;
a.g() = 0.0f;
a.b() = 0.0f;
a.a() = 128.5f;
BGRA<float> b = a;
std::cout << a << std::endl;
return 0;
}
If I compile the code below, I get an:
microsoft visual studio 12.0\vc\include\xrefwrap(58): error C2064: term does not evaluate to a function taking 2 arguments
In the call to accumulate algorithm, if I change the code to function<double(double, Position const&) > f = bind(sum, placeholders::_1, bind(mem_fn(&Position::getBalance), placeholders::_2));double sum = accumulate(balances.begin(), balances.end(), 0., f); everything compiles fine. I also tried to use a non member function, but it doesn't work neither.
class Position
{
private:
double m_balance;
public:
Position(double balance) :
m_balance(balance)
{}
double getBalance() const
{
return m_balance;
}
};
static double sum(double v1, double v2)
{
return v1 + v2;
}
int main(int argc, char ** argv)
{
std::vector< Position > balances;
for (size_t i = 0; i < 10; i++)
{
balances.push_back(Position(i));
}
double sum = accumulate(balances.begin(), balances.end(), 0., bind(sum, placeholders::_1, bind(mem_fn(&Position::getBalance), placeholders::_2)));
cout << sum << endl;
return 0;
}
This will fix it:
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
std::bind(std::plus<>(),
placeholders::_1,
std::bind(&Position::getBalance, placeholders::_2)));
or we could be kind to our fellow programmers:
auto add_balance = [](auto x, auto& position) {
return x + position.getBalance();
};
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
add_balance);
Or of course we can inline the lambda. There's no performance difference. Which one seems clearer will be a matter of personal preference.
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
[](auto x, auto& position)
{
return x + position.getBalance();
});
Or we can write a complex functor to do a similar job. This was the pre-lambda way:
template<class Op>
struct PositionOp
{
using mfp_type = double (Position::*)() const;
PositionOp(Op op, mfp_type mfp) : op(op), mfp(mfp) {}
template<class T>
auto operator()(T x, const Position& pos) const {
return op(x, (pos.*mfp)());
}
Op op;
mfp_type mfp;
};
template<class Op>
auto position_op(Op op, double (Position::*mfp)() const)
{
return PositionOp<Op>(op, mfp);
}
...
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
position_op(std::plus<>(), &Position::getBalance));
... but I hope you'll agree that this is ghastly.
I'm looking to create a lookup table of coordinates, something like:
int a[n][2] = {{0,1},{2,3}, ... }
For a given n, to be created at compile time. I started looking into constexpr, but is seems like a function returning a constexpr std::vector<std::array <int, 2> > isn't an option, as I get:
invalid return type 'std::vector<std::array<int, 2ul> >' of constexpr function
How can create such a compile time array?
With C++14, you do not need too much template magic. Here an example of how to have a lookup table for f(x) = x^3 with the first coordinate being the x value and the second coordinate being the f(x) value:
#include <iostream>
template<int N>
struct Table
{
constexpr Table() : values()
{
for (auto i = 0; i < N; ++i)
{
values[i][0] = i;
values[i][1] = i * i * i;
}
}
int values[N][2];
};
int main() {
constexpr auto a = Table<1000>();
for (auto x : a.values)
std::cout << "f(" << x[0] << ") = " << x[1] << '\n';
}
I'll dump the code first, adding references and comments where necessary/appropriate later. Just leave a comment if the result is somewhat close to what you're looking for.
Indices trick for pack expansion (required here to apply the generator), by Xeo, from this answer, modified to use std::size_t instead of unsigned.
#include <cstddef>
// by Xeo, from https://stackoverflow.com/a/13294458/420683
template<std::size_t... Is> struct seq{};
template<std::size_t N, std::size_t... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<std::size_t... Is>
struct gen_seq<0, Is...> : seq<Is...>{};
Generator function:
#include <array>
template<class Generator, std::size_t... Is>
constexpr auto generate_array_helper(Generator g, seq<Is...>)
-> std::array<decltype(g(std::size_t{}, sizeof...(Is))), sizeof...(Is)>
{
return {{g(Is, sizeof...(Is))...}};
}
template<std::size_t tcount, class Generator>
constexpr auto generate_array(Generator g)
-> decltype( generate_array_helper(g, gen_seq<tcount>{}) )
{
return generate_array_helper(g, gen_seq<tcount>{});
}
Usage example:
// some literal type
struct point
{
float x;
float y;
};
// output support for `std::ostream`
#include <iostream>
std::ostream& operator<<(std::ostream& o, point const& p)
{ return o << p.x << ", " << p.y; }
// a user-defined generator
constexpr point my_generator(std::size_t curr, std::size_t total)
{
return {curr*40.0f/(total-1), curr*20.0f/(total-1)};
}
int main()
{
constexpr auto first_array = generate_array<5>(my_generator);
constexpr auto second_array = generate_array<10>(my_generator);
std::cout << "first array: \n";
for(auto p : first_array)
{
std::cout << p << '\n';
}
std::cout << "========================\n";
std::cout << "second array: \n";
for(auto p : second_array)
{
std::cout << p << '\n';
}
}
What about using GNU gperf or some other code generation utility?
Is there an easy way to get a slice of an array in C++?
I.e., I've got
array<double, 10> arr10;
and want to get array consisting of five first elements of arr10:
array<double, 5> arr5 = arr10.???
(other than populating it by iterating through first array)
The constructors for std::array are implicitly defined so you can't initialize it with a another container or a range from iterators. The closest you can get is to create a helper function that takes care of the copying during construction. This allows for single phase initialization which is what I believe you're trying to achieve.
template<class X, class Y>
X CopyArray(const Y& src, const size_t size)
{
X dst;
std::copy(src.begin(), src.begin() + size, dst.begin());
return dst;
}
std::array<int, 5> arr5 = CopyArray<decltype(arr5)>(arr10, 5);
You can also use something like std::copy or iterate through the copy yourself.
std::copy(arr10.begin(), arr10.begin() + 5, arr5.begin());
Sure. Wrote this:
template<int...> struct seq {};
template<typename seq> struct seq_len;
template<int s0,int...s>
struct seq_len<seq<s0,s...>>:
std::integral_constant<std::size_t,seq_len<seq<s...>>::value> {};
template<>
struct seq_len<seq<>>:std::integral_constant<std::size_t,0> {};
template<int Min, int Max, int... s>
struct make_seq: make_seq<Min, Max-1, Max-1, s...> {};
template<int Min, int... s>
struct make_seq<Min, Min, s...> {
typedef seq<s...> type;
};
template<int Max, int Min=0>
using MakeSeq = typename make_seq<Min,Max>::type;
template<std::size_t src, typename T, int... indexes>
std::array<T, sizeof...(indexes)> get_elements( seq<indexes...>, std::array<T, src > const& inp ) {
return { inp[indexes]... };
}
template<int len, typename T, std::size_t src>
auto first_elements( std::array<T, src > const& inp )
-> decltype( get_elements( MakeSeq<len>{}, inp ) )
{
return get_elements( MakeSeq<len>{}, inp );
}
Where the compile time indexes... does the remapping, and MakeSeq makes a seq from 0 to n-1.
Live example.
This supports both an arbitrary set of indexes (via get_elements) and the first n (via first_elements).
Use:
std::array< int, 10 > arr = {0,1,2,3,4,5,6,7,8,9};
std::array< int, 6 > slice = get_elements(arr, seq<2,0,7,3,1,0>() );
std::array< int, 5 > start = first_elements<5>(arr);
which avoids all loops, either explicit or implicit.
2018 update, if all you need is first_elements:
Less boilerplaty solution using C++14 (building up on Yakk's pre-14 answer and stealing from "unpacking" a tuple to call a matching function pointer)
template < std::size_t src, typename T, int... I >
std::array< T, sizeof...(I) > get_elements(std::index_sequence< I... >, std::array< T, src > const& inp)
{
return { inp[I]... };
}
template < int N, typename T, std::size_t src >
auto first_elements(std::array<T, src > const& inp)
-> decltype(get_elements(std::make_index_sequence<N>{}, inp))
{
return get_elements(std::make_index_sequence<N>{}, inp);
}
Still cannot explain why this works, but it does (for me on Visual Studio 2017).
This answer might be late... but I was just toying around with slices - so here is my little home brew of std::array slices.
Of course, this comes with a few restrictions and is not ultimately general:
The source array from which a slice is taken must not go out of scope. We store a reference to the source.
I was looking for constant array slices first and did not try to expand this code to both const and non const slices.
But one nice feature of the code below is, that you can take slices of slices...
// ParCompDevConsole.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include "pch.h"
#include <cstdint>
#include <iostream>
#include <array>
#include <stdexcept>
#include <sstream>
#include <functional>
template <class A>
class ArraySliceC
{
public:
using Array_t = A;
using value_type = typename A::value_type;
using const_iterator = typename A::const_iterator;
ArraySliceC(const Array_t & source, size_t ifirst, size_t length)
: m_ifirst{ ifirst }
, m_length{ length }
, m_source{ source }
{
if (source.size() < (ifirst + length))
{
std::ostringstream os;
os << "ArraySliceC::ArraySliceC(<source>,"
<< ifirst << "," << length
<< "): out of bounds. (ifirst + length >= <source>.size())";
throw std::invalid_argument( os.str() );
}
}
size_t size() const
{
return m_length;
}
const value_type& at( size_t index ) const
{
return m_source.at( m_ifirst + index );
}
const value_type& operator[]( size_t index ) const
{
return m_source[m_ifirst + index];
}
const_iterator cbegin() const
{
return m_source.cbegin() + m_ifirst;
}
const_iterator cend() const
{
return m_source.cbegin() + m_ifirst + m_length;
}
private:
size_t m_ifirst;
size_t m_length;
const Array_t& m_source;
};
template <class T, size_t SZ>
std::ostream& operator<<( std::ostream& os, const std::array<T,SZ>& arr )
{
if (arr.size() == 0)
{
os << "[||]";
}
else
{
os << "[| " << arr.at( 0 );
for (auto it = arr.cbegin() + 1; it != arr.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
std::ostream& operator<<( std::ostream& os, const ArraySliceC<A> & slice )
{
if (slice.size() == 0)
{
os << "^[||]";
}
else
{
os << "^[| " << slice.at( 0 );
for (auto it = slice.cbegin() + 1; it != slice.cend(); it++)
{
os << "," << (*it);
}
os << " |]";
}
return os;
}
template<class A>
A unfoldArray( std::function< typename A::value_type( size_t )> producer )
{
A result;
for (size_t i = 0; i < result.size(); i++)
{
result[i] = producer( i );
}
return result;
}
int main()
{
using A = std::array<float, 10>;
auto idf = []( size_t i ) -> float { return static_cast<float>(i); };
const auto values = unfoldArray<A>(idf);
std::cout << "values = " << values << std::endl;
// zero copy slice of values array.
auto sl0 = ArraySliceC( values, 2, 4 );
std::cout << "sl0 = " << sl0 << std::endl;
// zero copy slice of the sl0 (the slice of values array)
auto sl01 = ArraySliceC( sl0, 1, 2 );
std::cout << "sl01 = " << sl01 << std::endl;
return 0;
}
in this question:
template; Point<2, double>; Point<3, double>
Dennis and Michael noticed the unreasonable foolishly implemented constructor.
They were right, I didn't consider this at that moment.
But I found out that a constructor does not help very much for a template class like this one, instead a function is here much more convenient and safe
namespace point {
template < unsigned int dims, typename T >
struct Point {
T X[ dims ];
std::string str() {
std::stringstream s;
s << "{";
for ( int i = 0; i < dims; ++i ) {
s << " X" << i << ": " << X[ i ] << (( i < dims -1 )? " |": " ");
}
s << "}";
return s.str();
}
Point<dims, int> toint() {
Point<dims, int> ret;
std::copy( X, X+dims, ret.X );
return ret;
}
};
template < typename T >
Point< 2, T > Create( T X0, T X1 ) {
Point< 2, T > ret;
ret.X[ 0 ] = X0; ret.X[ 1 ] = X1;
return ret;
}
template < typename T >
Point< 3, T > Create( T X0, T X1, T X2 ) {
Point< 3, T > ret;
ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2;
return ret;
}
template < typename T >
Point< 4, T > Create( T X0, T X1, T X2, T X3 ) {
Point< 4, T > ret;
ret.X[ 0 ] = X0; ret.X[ 1 ] = X1; ret.X[ 2 ] = X2; ret.X[ 3 ] = X3;
return ret;
}
};
int main( void ) {
using namespace point;
Point< 2, double > p2d = point::Create( 12.3, 34.5 );
Point< 3, double > p3d = point::Create( 12.3, 34.5, 56.7 );
Point< 4, double > p4d = point::Create( 12.3, 34.5, 56.7, 78.9 );
//Point< 3, double > p1d = point::Create( 12.3, 34.5 ); //no suitable user defined conversion exists
//Point< 3, int > p1i = p4d.toint(); //no suitable user defined conversion exists
Point< 2, int > p2i = p2d.toint();
Point< 3, int > p3i = p3d.toint();
Point< 4, int > p4i = p4d.toint();
std::cout << p2d.str() << std::endl;
std::cout << p3d.str() << std::endl;
std::cout << p4d.str() << std::endl;
std::cout << p2i.str() << std::endl;
std::cout << p3i.str() << std::endl;
std::cout << p4i.str() << std::endl;
char c;
std::cin >> c;
}
has the new C++ standard any new improvements, language features or simplifications regarding this aspect of ctor of a template class?
what do you think about the implementation of the combination of namespace, stuct and Create function?
many thanks in advance
Oops
Since the array is public, it is an option to omit the constructor and allow aggregate initialization (like boost::array<T, N> for example).
Point<2, int> p = {1, 2};
This is no worse than having to call a create function. (The create function might still be handy as a utility.)
In C++0x you will be able to have all sorts of coolness. For example, play with variadic templates, to have it checked at compile-time if the constructor is called with a right number of arguments. (The following could also check if the arguments ...U are all of type T, with some more metaprogramming fun, but it might not be absolutely necessary.)
//helper to copy variable amount of arguments into an array
namespace detail {
template <class T, class U>
void copy_variadic(T* p, U value)
{
*p = value;
}
template <class T, class First, class ...Rest>
void copy_variadic(T* p, First var, Rest ...args)
{
*p = var;
copy_variadic(++p, args...);
}
} //detail
template < unsigned int dims, typename T >
struct Point {
T X[ dims ];
Point() : X{}
{
}
template <class ...U>
Point(U... args) {
static_assert(sizeof...(args) == dims, "Too many or too few arguments to Point constructor");
detail::copy_variadic(X, args...);
}
//...
};
(Actually, with some modifications - perfect forwarding - copy_variadic would make a nice addition to my collection of variadic-template utilities, if someone doesn't come and point out a significantly better way.)
Yes, as Michael pointed out in his answer to your previous question, in C++0x you'll be able to use an initializer list to pass an arbitrary number of arguments to your ctor. In your case, the code would look something like:
template <int dims, class T>
class point {
T X[dims];
public:
point(std::initializer_list<T> const &init) {
std::copy(init.begin(), init.begin()+dims, X);
}
};
You could create a point object with this something like:
point<3, double> x{0.0, 0.0, 0.0};
Personally, I'm not sure I like the basic design very well though. In particular, I'd rather see X turned into an std::vector, and determine the number of dimensions strictly from the parameter list that was passed instead of having it as a template argument:
template <class T>
class point {
std::vector<T> X;
public:
point(std::initializer_list<T> init) {
std::copy(init.begin(), init.end(), std::back_inserter(X));
}
};
This does have some trade-offs though -- points with a different number of dimensions are still the same type. For example, it basically asserts that it's reasonable to assign a 2D point to a 3D point, or vice versa.