Constructors, templates and non-type parameters

前端 未结 3 1364
没有蜡笔的小新
没有蜡笔的小新 2021-01-01 16:47

I\'ve a class that must depend for some reasons from an int template parameter.
For the same reasons, that parameter cannot be part of the parameter list fo

相关标签:
3条回答
  • 2021-01-01 17:10

    I think that solution with "traits" is the best for most cases.

    Only to make a little more "mess" in this issue I will provide two more alternatives. Maybe in some very specific cases - they can be in some way better.


    1. Template global variable - you can name it a prototype solution:

    class C only differs in its constructor from your original code:

    class C final {
        // All B and D defined as in OP code
     public:
        // Here the change - c-tor just accepts D<int> 
        template <int size>
        explicit C(D<size>* b) : b(b) {}
    
        // all the rest as in OP code
    };
    

    The prototype - template global variable:

    template <int N>
    C c{new C::D<N>()}; 
    // this variable should be rather const - but foo() is not const 
    // and copy semantic is not implemented...
    

    And usage:

    int main() {
        // you did not implement copy semantic properly - so just reference taken
        C& c = ::c<3>; 
        c.foo();
    }
    

    1. Solution with Base class - and derive class depending on int

    This solution, although looks pretty promising I would personally avoid - that only complicates design - and some possibility of object slicing is here present too.

    class CBase {
        // all here as in OP code for C class
    public:
        // only difference is this constructor:
        template<int size>
        explicit CBase(D<size>* b) : b(b) {}
    };
    

    Then - the final class:

    template <int N>
    class C final : private CBase {
    public:
        C() : CBase(new CBase::D<N>()) {}
        using CBase::foo;
    };
    

    The usage:

    int main() {
        C<3> c;
        c.foo();
    }
    

    Q: One can ask in which way solution with Base class is better than just adding int as another parameter.
    A: by base implementation class you do not need to have many "copies" of the same code - you avoid template code bloat...

    0 讨论(0)
  • 2021-01-01 17:28

    You have to pass in something that can be deduced. The simplest thing to use is just a empty wrapper for an int: std::integral_constant. Since you only want ints I believe, we can alias it and then only accept that specific type:

    template <int N>
    using int_ = std::integral_constant<int, N>;
    

    Where your C constructor just accepts that:

     template <int N>
     explicit C(int_<N> ) {
         b = new D<N>{};
     }
    
     C c{int_<3>{}};
    

    You could even go all out and create a user-defined literal for this (a la Boost.Hana) so that you can write:

    auto c = 3_c; // does the above
    

    Also, consider simply forwarding the trait through to D. Metaprogramming works better if everything everywhere is a type. That is, still accept the same int_ in C:

    template <class T>
    explicit C(T ) {
        b = new D<T>{};
    }
    

    Where now D expects something that has a ::value:

    template <class T>
    struct D: public B{
        static constexpr int N = T::value;
    
        void foo() {
            using namespace std;
            cout << N << endl;
        }
    
        std::array<int, N> arr;
    };
    

    It's the same thing either way from the user of C's perspective, but just worth a thought.

    0 讨论(0)
  • 2021-01-01 17:30

    use template specialization and inheritance:

    #include <iostream>
    using namespace std;
    
    template <int num> struct A {
        A() { cout << "generic" << endl; }
    };
    
    template <> struct A<1> {
        A() { cout << "implementation of 1" << endl; }
    };
    
    template <int num>
    struct B : public A<num> {
        B() : A<num>() {}
    };
    
    int main(int argc, char *argv[])
    {
        B<1> b;
        B<3> b1;
        B<4> b2;
    }
    

    edit: or you can do it even easier:

    template <int num>
    struct A {
        A();
    };
    
    template <int num>
    A<num>::A() { cout << "general " << num << endl; }
    
    template <>
    A<1>::A() { cout << "specialization for 1" << endl; }
    
    0 讨论(0)
提交回复
热议问题