which is better?

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • wogston

    which is better?


    (A)

    template <typename T>
    metainline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
    {
    foo<T> v;
    repeat< 4,exec_mul<T> >::exec(v,a,b,t ime);
    return v;
    }

    (B)

    template <typename T>
    inline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
    {
    return foo<T>(
    a[0] + (b[0] - a[0]) * time,
    a[1] + (b[1] - a[1]) * time,
    a[2] + (b[2] - a[2]) * time,
    a[3] + (b[3] - a[3]) * time);
    }

    Which of the above: (A) or (B) is "better" from the viewpoint of the
    language police? I am mainly interested in efficiency (assuming correctness
    is met in both cases). I'm unrolling repeatitive tasks in (A), but I would
    like to know if this is the famous NRVO I keep hearing about ; the return
    value is named.. but is (B) theoretically more efficient, excluding possible
    overhead from repeat<> template?

    Here's the repeat and exec_mul in case they are relevant:

    template <typename SCALAR>
    struct exec_lerp
    {
    template <typename A, typename B, typename C>
    static metainline void exec(int index, A& a, const B& b, const C& c,
    const SCALAR& d)
    {
    a[index] = b[index] + (c[index] - b[index]) * d;
    }
    };

    template <int SIZE, typename E>
    struct repeat
    {
    enum { INDEX = SIZE - 1 };

    template <typename A, typename B, typename C>
    static metainline void exec(A& a, const B& b, const C& c)
    {
    repeat<INDEX,E> ::exec(a,b,c);
    E::exec(INDEX,a ,b,c);
    }

    template <typename A, typename B>
    static metainline void exec(A& a, const B& b)
    {
    repeat<INDEX,E> ::exec(a,b);
    E::exec(INDEX,a ,b);
    }

    template <typename A>
    static metainline void exec(A& a)
    {
    repeat<INDEX,E> ::exec(a);
    E::exec(INDEX,a );
    }
    };

    In otherwords, is this case of Named Return Value Optimization? While I am
    aware that C++ doesn't recognize such concept, but contemporary compilers do
    and this isn't std.c++ but rather generic C++ group I thought to ask here to
    learn more: I'm interested in performance (and correctness). The "foo" type
    has const and non-const [] operator, through which it accesses the
    components, the internal presentation is static array, in most cases like
    this:

    template <typename SCALAR, int SIZE>
    class foo
    {
    protected:
    SCALAR m_v[SIZE];
    // ...
    };

    Ie. is it generally (with current compilers!) prefered to return-by-value
    constructing the return value object in return statement, or give it a name
    and return?

    I posted on same topic few years back on my opinions on my current vector
    library, where I named the members:

    SCALAR x,y,z,w; // example

    These are possible to initialize in constructor using initializers, array
    isn't.. but array was recommended back then so I decided to give it a shot
    in the current design, so from this point of view it doesn't make any
    difference if I try to initialize the object in constructor or just unroll
    and initialize components with a template.

    Any thoughts? Criticism? Helpful suggestions?


    --
    "I seen things you couldn't imagine.." - B.B. Warner
    "I seen you do the them and it wasn't pretty.." - Anonymous


  • Alf P. Steinbach

    #2
    Re: which is better?

    On Sat, 27 Sep 2003 01:53:38 +0300, "wogston" <spam@nothere.n et> wrote:[color=blue]
    >
    >(A)
    >
    >template <typename T>
    >metainline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)[/color]

    C++ has no keyword 'metainline', and you don't define a 'metainline'
    macro in the code presented here.

    Right here you're already in off-topic non-C++ land.


    [color=blue]
    >{
    > foo<T> v;
    > repeat< 4,exec_mul<T> >::exec(v,a,b,t ime);
    > return v;
    >}
    >
    >(B)
    >
    >template <typename T>
    >inline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
    >{
    > return foo<T>(
    > a[0] + (b[0] - a[0]) * time,
    > a[1] + (b[1] - a[1]) * time,
    > a[2] + (b[2] - a[2]) * time,
    > a[3] + (b[3] - a[3]) * time);
    >}
    >
    >Which of the above: (A) or (B) is "better"[/color]

    Asking for "better" is trolling.


    [color=blue]
    >from the viewpoint of the language police?[/color]

    Using deragatory terms like "language police" is trolling.


    [color=blue]
    >I am mainly interested in efficiency (assuming correctness
    >is met in both cases). I'm unrolling repeatitive tasks in (A), but I would
    >like to know if this is the famous NRVO I keep hearing about ;[/color]

    It is not.

    [color=blue]
    >the return value is named..[/color]

    It is not.


    [color=blue]
    > but is (B) theoretically more efficient, excluding possible
    >overhead from repeat<> template?[/color]

    That is a Quality Of Implementation issue (also known as trolling).

    Assuming the compiler does not optimize, the version with a single
    'return'-statement should generally be more efficient than the one
    with additional statements.

    There is no answer without such assumptions.


    [color=blue]
    >Here's the repeat and exec_mul in case they are relevant:
    >
    >template <typename SCALAR>
    >struct exec_lerp
    >{
    > template <typename A, typename B, typename C>
    > static metainline void exec(int index, A& a, const B& b, const C& c,[/color]

    As mentioned, C++ has no keyword 'metainline'.


    [color=blue]
    >const SCALAR& d)
    > {
    > a[index] = b[index] + (c[index] - b[index]) * d;
    > }
    >};
    >
    >template <int SIZE, typename E>
    >struct repeat
    >{
    > enum { INDEX = SIZE - 1 };
    >
    > template <typename A, typename B, typename C>
    > static metainline void exec(A& a, const B& b, const C& c)
    > {
    > repeat<INDEX,E> ::exec(a,b,c);
    > E::exec(INDEX,a ,b,c);
    > }
    >
    > template <typename A, typename B>
    > static metainline void exec(A& a, const B& b)
    > {
    > repeat<INDEX,E> ::exec(a,b);
    > E::exec(INDEX,a ,b);
    > }
    >
    > template <typename A>
    > static metainline void exec(A& a)
    > {
    > repeat<INDEX,E> ::exec(a);
    > E::exec(INDEX,a );
    > }
    >};
    >
    >In otherwords, is this case of Named Return Value Optimization?[/color]

    No (NRVO is a language extension that you don't use here).

    [color=blue]
    > While I am
    >aware that C++ doesn't recognize such concept, but contemporary compilers do
    >and this isn't std.c++ but rather generic C++ group[/color]

    That is incorrect.

    Read Shiva's welcome-text.

    Read the FAQ.


    [color=blue]
    > I thought to ask here to
    >learn more: I'm interested in performance (and correctness). The "foo" type
    >has const and non-const [] operator, through which it accesses the
    >components, the internal presentation is static array, in most cases like
    >this:
    >
    >template <typename SCALAR, int SIZE>
    >class foo
    >{
    >protected:
    > SCALAR m_v[SIZE];
    >// ...
    >};[/color]

    Why don't you use a std::vector<>?


    [color=blue]
    >Ie. is it generally (with current compilers!) prefered to return-by-value
    >constructing the return value object in return statement, or give it a name
    >and return?[/color]

    Whatever gives clear, readable code.


    [color=blue]
    >I posted on same topic few years back on my opinions on my current vector
    >library, where I named the members:
    >
    >SCALAR x,y,z,w; // example
    >
    >These are possible to initialize in constructor using initializers, array
    >isn't.. but array was recommended back then so I decided to give it a shot
    >in the current design, so from this point of view it doesn't make any
    >difference if I try to initialize the object in constructor or just unroll
    >and initialize components with a template.
    >
    >Any thoughts? Criticism? Helpful suggestions?[/color]

    Try to describe what you're trying to achieve, not your technical solution.

    Comment

    • wogston

      #3
      Re: which is better?

      > C++ has no keyword 'metainline', and you don't define a 'metainline'[color=blue]
      > macro in the code presented here.
      >
      > Right here you're already in off-topic non-C++ land.[/color]

      My bad, it was copy-paste from "real code", not typed specificly to here.
      It's a macro for __forceinline or inline, depending on what compiler you
      happen to be on. OK, so __forceinline is off-topic here, assume it reads
      "inline" from this point forward.


      Comment

      • wogston

        #4
        Re: which is better?

        > > C++ has no keyword 'metainline', and you don't define a 'metainline'[color=blue][color=green]
        > > macro in the code presented here.[/color][/color]

        Here are the full definitions. hope this helps (somehow I doubt it, I tried
        to trim it down to minimum previous time). If there are further headers you
        would like to see before staying on-topic let me know. ;-)



        // begin "configure. hpp"

        #ifndef PRMATH_CONFIGUR E_HPP
        #define PRMATH_CONFIGUR E_HPP
        namespace prmath
        {
        // ------------------------------------------------------------
        // Microsoft Visual C++
        // ------------------------------------------------------------
        #if defined(__VISUA LC__)
        #pragma inline_depth(25 5)
        #pragma inline_recursio n(on)
        #pragma auto_inline(on)
        #ifndef metainline
        #define metainline __forceinline
        #endif
        #ifndef PRMATH_EXPRESSI ON_ENABLE
        #define PRMATH_EXPRESSI ON_ENABLE
        #endif
        #endif
        // ------------------------------------------------------------
        // Intel C++
        // ------------------------------------------------------------
        #if defined(__INTEL _COMPILER)
        #ifndef metainline
        #define metainline __forceinline
        #endif
        #endif
        // ------------------------------------------------------------
        // generic c++ compiler
        // ------------------------------------------------------------
        #ifndef metainline
        #define metainline inline
        #endif
        #ifdef PRMATH_EXPRESSI ON_DISABLE
        #ifdef PRMATH_EXPRESSI ON_ENABLE
        #undef PRMATH_EXPRESSI ON_ENABLE
        #endif
        #endif
        #ifdef PRMATH_TYPENAME _DISABLE
        #ifdef PRMATH_TYPENAME _ENABLE
        #undef PRMATH_TYPENAME _ENABLE
        #endif
        #else
        #ifndef PRMATH_TYPENAME _ENABLE
        #define PRMATH_TYPENAME _ENABLE
        #endif
        #endif
        } // namespace prmath
        #endif


        // begin "evaluate.h pp"

        #ifndef PRMATH_EVALUATE _HPP
        #define PRMATH_EVALUATE _HPP
        #include <cassert>
        #include "configure. hpp"
        namespace prmath
        {
        // ------------------------------------------------------------
        // permute masks
        // ------------------------------------------------------------
        enum
        {
        xxx = 0, yxx = 1, zxx = 2,
        xyx = 4, yyx = 5, zyx = 6,
        xzx = 8, yzx = 9, zzx = 10,
        xxy = 16, yxy = 17, zxy = 18,
        xyy = 20, yyy = 21, zyy = 22,
        xzy = 24, yzy = 25, zzy = 26,
        xxz = 32, yxz = 33, zxz = 34,
        xyz = 36, yyz = 37, zyz = 38,
        xzz = 40, yzz = 41, zzz = 42
        };
        enum
        {
        xxxx,yxxx,zxxx, wxxx,xyxx,yyxx, zyxx,wyxx,xzxx, yzxx,zzxx,wzxx, xwxx,ywxx,zwxx, w
        wxx,
        xxyx,yxyx,zxyx, wxyx,xyyx,yyyx, zyyx,wyyx,xzyx, yzyx,zzyx,wzyx, xwyx,ywyx,zwyx, w
        wyx,
        xxzx,yxzx,zxzx, wxzx,xyzx,yyzx, zyzx,wyzx,xzzx, yzzx,zzzx,wzzx, xwzx,ywzx,zwzx, w
        wzx,
        xxwx,yxwx,zxwx, wxwx,xywx,yywx, zywx,wywx,xzwx, yzwx,zzwx,wzwx, xwwx,ywwx,zwwx, w
        wwx,
        xxxy,yxxy,zxxy, wxxy,xyxy,yyxy, zyxy,wyxy,xzxy, yzxy,zzxy,wzxy, xwxy,ywxy,zwxy, w
        wxy,
        xxyy,yxyy,zxyy, wxyy,xyyy,yyyy, zyyy,wyyy,xzyy, yzyy,zzyy,wzyy, xwyy,ywyy,zwyy, w
        wyy,
        xxzy,yxzy,zxzy, wxzy,xyzy,yyzy, zyzy,wyzy,xzzy, yzzy,zzzy,wzzy, xwzy,ywzy,zwzy, w
        wzy,
        xxwy,yxwy,zxwy, wxwy,xywy,yywy, zywy,wywy,xzwy, yzwy,zzwy,wzwy, xwwy,ywwy,zwwy, w
        wwy,
        xxxz,yxxz,zxxz, wxxz,xyxz,yyxz, zyxz,wyxz,xzxz, yzxz,zzxz,wzxz, xwxz,ywxz,zwxz, w
        wxz,
        xxyz,yxyz,zxyz, wxyz,xyyz,yyyz, zyyz,wyyz,xzyz, yzyz,zzyz,wzyz, xwyz,ywyz,zwyz, w
        wyz,
        xxzz,yxzz,zxzz, wxzz,xyzz,yyzz, zyzz,wyzz,xzzz, yzzz,zzzz,wzzz, xwzz,ywzz,zwzz, w
        wzz,
        xxwz,yxwz,zxwz, wxwz,xywz,yywz, zywz,wywz,xzwz, yzwz,zzwz,wzwz, xwwz,ywwz,zwwz, w
        wwz,
        xxxw,yxxw,zxxw, wxxw,xyxw,yyxw, zyxw,wyxw,xzxw, yzxw,zzxw,wzxw, xwxw,ywxw,zwxw, w
        wxw,
        xxyw,yxyw,zxyw, wxyw,xyyw,yyyw, zyyw,wyyw,xzyw, yzyw,zzyw,wzyw, xwyw,ywyw,zwyw, w
        wyw,
        xxzw,yxzw,zxzw, wxzw,xyzw,yyzw, zyzw,wyzw,xzzw, yzzw,zzzw,wzzw, xwzw,ywzw,zwzw, w
        wzw,
        xxww,yxww,zxww, wxww,xyww,yyww, zyww,wyww,xzww, yzww,zzww,wzww, xwww,ywww,zwww, w
        www
        };
        // ------------------------------------------------------------
        // execs
        // ------------------------------------------------------------
        template <typename SCALAR>
        struct exec_copy
        {
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] = b[index];
        }
        template <typename A>
        static metainline void exec(int index, A& a, const SCALAR& b)
        {
        a[index] = b;
        }
        };
        template <typename SCALAR>
        struct exec_min
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c)
        {
        a[index] = b[index] < c[index] ? b[index] : c[index];
        }
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] = a[index] < b[index] ? a[index] : b[index];
        }
        };
        template <typename SCALAR>
        struct exec_max
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c)
        {
        a[index] = b[index] > c[index] ? b[index] : c[index];
        }
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] = a[index] > b[index] ? a[index] : b[index];
        }
        };
        template <typename SCALAR>
        struct exec_neg
        {
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] = -b[index];
        }
        template <typename A>
        static metainline void exec(int index, A& a)
        {
        a[index] = -a[index];
        }
        };
        template <typename SCALAR>
        struct exec_add
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c)
        {
        a[index] = b[index] + c[index];
        }
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] += b[index];
        }
        };
        template <typename SCALAR>
        struct exec_sub
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c)
        {
        a[index] = b[index] - c[index];
        }
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] -= b[index];
        }
        };
        template <typename SCALAR>
        struct exec_mul
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c)
        {
        a[index] = b[index] * c[index];
        }
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b)
        {
        a[index] *= b[index];
        }
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b, const SCALAR& c)
        {
        a[index] = b[index] * c;
        }
        template <typename A>
        static metainline void exec(int index, A& a, const SCALAR& b)
        {
        a[index] *= b;
        }
        };
        template <typename SCALAR, int SIZE>
        struct exec_cross
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int, A&, const B&, const C&)
        {
        }
        };
        template <typename SCALAR>
        struct exec_cross<SCAL AR,3>
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c)
        {
        switch ( index )
        {
        case 0: a[0] = b[1] * c[2] - b[2] * c[1]; break;
        case 1: a[1] = b[2] * c[0] - b[0] * c[2]; break;
        case 2: a[2] = b[0] * c[1] - b[1] * c[0]; break;
        }
        }
        };
        template <typename SCALAR>
        struct exec_lerp
        {
        template <typename A, typename B, typename C>
        static metainline void exec(int index, A& a, const B& b, const C& c, const
        SCALAR& d)
        {
        a[index] = b[index] + (c[index] - b[index]) * d;
        }
        };
        template <typename SCALAR>
        struct exec_permute
        {
        template <typename A, typename B>
        static metainline void exec(int index, A& a, const B& b, const int& mask)
        {
        const int idx = (mask >> (index * 2)) & 0x03;
        a[index] = b[idx];
        }
        };
        // ------------------------------------------------------------
        // repeat
        // ------------------------------------------------------------
        template <int SIZE, typename E>
        struct repeat
        {
        enum { INDEX = SIZE - 1 };
        template <typename A, typename B, typename C>
        static metainline void exec(A& a, const B& b, const C& c)
        {
        repeat<INDEX,E> ::exec(a,b,c);
        E::exec(INDEX,a ,b,c);
        }
        template <typename A, typename B>
        static metainline void exec(A& a, const B& b)
        {
        repeat<INDEX,E> ::exec(a,b);
        E::exec(INDEX,a ,b);
        }
        template <typename A>
        static metainline void exec(A& a)
        {
        repeat<INDEX,E> ::exec(a);
        E::exec(INDEX,a );
        }
        };
        template <typename E>
        struct repeat<3,E>
        {
        template <typename A, typename B, typename C>
        static metainline void exec(A& a, const B& b, const C& c)
        {
        E::exec(0,a,b,c );
        E::exec(1,a,b,c );
        E::exec(2,a,b,c );
        }
        template <typename A, typename B>
        static metainline void exec(A& a, const B& b)
        {
        E::exec(0,a,b);
        E::exec(1,a,b);
        E::exec(2,a,b);
        }
        template <typename A>
        static metainline void exec(A& a)
        {
        E::exec(0,a);
        E::exec(1,a);
        E::exec(2,a);
        }
        };
        template <typename E>
        struct repeat<2,E>
        {
        template <typename A, typename B, typename C>
        static metainline void exec(A& a, const B& b, const C& c)
        {
        E::exec(0,a,b,c );
        E::exec(1,a,b,c );
        }
        template <typename A, typename B>
        static metainline void exec(A& a, const B& b)
        {
        E::exec(0,a,b);
        E::exec(1,a,b);
        }
        template <typename A>
        static metainline void exec(A& a)
        {
        E::exec(0,a);
        E::exec(1,a);
        }
        };
        template <typename E>
        struct repeat<1,E>
        {
        template <typename A, typename B, typename C>
        static metainline void exec(A& a, const B& b, const C& c)
        {
        E::exec(0,a,b,c );
        }
        template <typename A, typename B>
        static metainline void exec(A& a, const B& b)
        {
        E::exec(0,a,b);
        }
        template <typename A>
        static metainline void exec(A& a)
        {
        E::exec(0,a);
        }
        };
        // ------------------------------------------------------------
        // product
        // ------------------------------------------------------------
        template <typename SCALAR, int SIZE>
        struct product
        {
        enum { INDEX = SIZE - 1 };
        template <typename A, typename B>
        static metainline SCALAR exec(const A& a, const B& b)
        {
        return product<SCALAR, INDEX>::exec(a, b) + a[INDEX] * b[INDEX];
        }
        };
        template <typename SCALAR>
        struct product<SCALAR, 4>
        {
        template <typename A, typename B>
        static metainline SCALAR exec(const A& a, const B& b)
        {
        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
        }
        };
        template <typename SCALAR>
        struct product<SCALAR, 3>
        {
        template <typename A, typename B>
        static metainline SCALAR exec(const A& a, const B& b)
        {
        return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
        }
        };
        template <typename SCALAR>
        struct product<SCALAR, 2>
        {
        template <typename A, typename B>
        static metainline SCALAR exec(const A& a, const B& b)
        {
        return a[0] * b[0] + a[1] * b[1];
        }
        };
        template <typename SCALAR>
        struct product<SCALAR, 1>
        {
        template <typename A, typename B>
        static metainline SCALAR exec(const A& a, const B& b)
        {
        return a[0] * b[0];
        }
        };
        // ------------------------------------------------------------
        // evaluators
        // ------------------------------------------------------------
        template <typename SCALAR>
        struct eval_ref
        {
        template <typename A>
        static metainline SCALAR evaluate(int index, const A& a, const int&)
        {
        return a[index];
        }
        };
        template <typename SCALAR>
        struct eval_neg
        {
        template <typename A>
        static metainline SCALAR evaluate(int index, const A& a, const int&)
        {
        return -a[index];
        }
        };
        template <typename SCALAR>
        struct eval_add
        {
        template <typename A, typename B>
        static metainline SCALAR evaluate(int index, const A& a, const B& b)
        {
        return a[index] + b[index];
        }
        };
        template <typename SCALAR>
        struct eval_sub
        {
        template <typename A, typename B>
        static metainline SCALAR evaluate(int index, const A& a, const B& b)
        {
        return a[index] - b[index];
        }
        };
        template <typename SCALAR>
        struct eval_mul
        {
        template <typename A>
        static metainline SCALAR evaluate(int index, const A& a, const SCALAR b)
        {
        return a[index] * b;
        }
        template <typename B>
        static metainline SCALAR evaluate(int index, const SCALAR a, const B& b)
        {
        return a * b[index];
        }
        };
        template <typename SCALAR>
        struct eval_div
        {
        template <typename A>
        static metainline SCALAR evaluate(int index, const A& a, const SCALAR b)
        {
        return a[index] / b;
        }
        };
        template <typename SCALAR, int SIZE>
        struct eval_cross
        {
        template <typename A, typename B>
        static metainline SCALAR evaluate(int, const A&, const B&)
        {
        return 0;
        }
        };
        template <typename SCALAR>
        struct eval_cross<SCAL AR,3>
        {
        template <typename A, typename B>
        static metainline SCALAR evaluate(int index, const A& a, const B& b)
        {
        switch ( index )
        {
        case 0: return a[1] * b[2] - a[2] * b[1];
        case 1: return a[2] * b[0] - a[0] * b[2];
        case 2: return a[0] * b[1] - a[1] * b[0];
        default: return 0;
        }
        }
        };
        template <typename SCALAR, int SIZE>
        struct eval_permute
        {
        template <typename A>
        static metainline SCALAR evaluate(int index, const A& a, const int& mask)
        {
        index = (mask >> (index * 2)) & 0x03;
        assert( index < SIZE );
        return a[index];
        }
        };
        // ------------------------------------------------------------
        // expression
        // ------------------------------------------------------------
        template <typename TYPE, typename SCALAR, int SIZE, typename EVALUATOR,
        typename A, typename B>
        class expr
        {
        private:
        const A m_a;
        const B m_b;
        public:
        metainline expr(const A& a, const B& b)
        : m_a(a), m_b(b)
        {
        }
        metainline SCALAR operator [] (int index) const
        {
        assert( index >= 0 && index < SIZE );
        return EVALUATOR::eval uate(index,m_a, m_b);
        }
        metainline expr operator + () const
        {
        return *this;
        }
        metainline expr< TYPE,SCALAR,SIZ E,eval_neg<SCAL AR>,expr,int >
        operator - () const
        {
        return expr< TYPE,SCALAR,SIZ E,eval_neg<SCAL AR>,expr,int >(*this,0);
        }
        };
        } // namespace prmath
        #endif


        --
        "What You See isn't What You Get, But What I Give..." -- N. B.


        Comment

        Working...