Using an int as a template parameter that is not known until run-time

后端 未结 5 1473
谎友^
谎友^ 2021-01-02 19:04

I am trying to use an integer as a template parameter for a class. Here is a sample of the code:

template< int array_qty > 
class sample_class {

    p         


        
相关标签:
5条回答
  • 2021-01-02 19:13

    Sorry, this is not possible. The template argument must be a constant expression known at compile time.

    0 讨论(0)
  • 2021-01-02 19:27

    This can be done in effect. But trust me when I say you are asking the wrong question. So what follows answers your question, even thought doing it is a bad idea almost always.

    What you in effect can do is create 50 different programs, one for each of the 50 possible sizes, and then conditionally jump to the one you want.

    template<int n>
    struct prog {
      void run() {
        // ...
      }
    };
    
    
    template<int n>
    struct switcher {
      void run(int v) {
        if(v==n)
          prog<n>::run();
        else
          switcher<n-1>::run(v);
      }
    };
    
    template<>
    struct switcher<-1> {
      void run(int v){
      }
    };
    

    Call switcher<50>::run( value ); and if value is 0 to 50, prog<value>::run() is invoked. Within prog::run the template parameter is a compile time value.

    Horrid hack, and odds are you would be better off using another solution, but it is what you asked for.

    Here is a C++14 table-based version:

    template<size_t N>
    using index_t = std::integral_constant<size_t, N>; // C++14
    
    template<size_t M>
    struct magic_switch_t {
      template<class F, class...Args>
      using R=std::result_of_t<F(index_t<0>, Args...)>;
      template<class F, class...Args>
      R<F, Args...> operator()(F&& f, size_t i, Args&&...args)const{
        if (i >= M)
          throw i; // make a better way to return an error
        return invoke(std::make_index_sequence<M>{}, std::forward<F>(f), i, std::forward<Args>(args)...);
      }
    private:
      template<size_t...Is, class F, class...Args>
      R<F, Args...> invoke(std::index_sequence<Is...>, F&&f, size_t i, Args&&...args)const {
        using pF=decltype(std::addressof(f));
        using call_func = R<F, Args...>(*)(pF pf, Args&&...args);
        static const call_func table[M]={
          [](pF pf, Args&&...args)->R<F, Args...>{
            return std::forward<F>(*pf)(index_t<Is>{}, std::forward<Args>(args)...);
          }...
        };
        return table[i](std::addressof(f), std::forward<Args>(args)...);
      }
    };
    

    magic_switch_t<N>{}( f, 3, blah1, blah2, etc ) will invoke f(index_t<3>{}, blah1, blah2, etc).

    Some C++14 compilers will choke on the variardic pack expansion containing a lambda. It isn't essential, you can do a workaround, but the workaround is ugly.

    The C++14 features are all optional: you can implement it all in C++11, but again, ugly.

    The f passed basically should be a function object (either a lambda taking auto as the first argument, or a manual one). Passing a function name directly won't work well, because the above best works when the first argument becomes a compile-time value.

    You can wrap a function template with a lambda or a function object to help.

    0 讨论(0)
  • 2021-01-02 19:31

    For C++ 11, non-type template arguments are restricted to the following (§14.3.2/1):

    A template-argument for a non-type, non-template template-parameter shall be one of:

    • for a non-type template-parameter of integral or enumeration type, a converted constant expression (5.19) of the type of the template-parameter; or
    • the name of a non-type template-parameter; or
    • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
    • a constant expression that evaluates to a null pointer value (4.10); or
    • a constant expression that evaluates to a null member pointer value (4.11); or
    • a pointer to member expressed as described in 5.3.1.

    In C++ 98 and 03, the list is even more restricted. Bottom line: what you're trying to do simply isn't allowed.

    0 讨论(0)
  • 2021-01-02 19:33

    I'm a little late, but here's my suggestion. I'm guessing the main problem with vectors for you is that they allocate a larger capacity than what you need in order to support dynamic growth. So, can't you write your own simple array class?

    template <typename T>
    class Array {
        private: 
            T* data; 
            unsigned size; 
        public: 
            Array(unsigned size) {
                data = new T[size]; 
                this->size = size; 
            }
            T& operator[](int i) {
                return data[i]; 
            }
            T operator[](int i) const {
                return data[i]; 
            }
    
            // Depending on your needs, maybe add copy constructor and assignment operator here.
            ...
    
            unsigned size() {
                return size; 
            }
    
            ~Array() {
                delete [] data; 
            }
    }
    

    From what I know, I believe this should be just as fast as the STL array class. In addition, you can create Arrays with sizes unknown until run-time, the Array's memory is handled automatically when it is destroyed, and you don't have to instantiate a new class every time you create a new array with a different size (like you have to do for STL arrays).

    0 讨论(0)
  • 2021-01-02 19:36

    Template arguments must be compile-time constants aka "constant expressions" or constexprs for short. So there is no way to do is using templates.

    You could use a dynamic-sized array and store its size in an int.

    Or simply use a vector. Be sure to initialize its size in the constructor by passing the desired size to the vector's constructor!

    0 讨论(0)
提交回复
热议问题