C++ Template Specialization with Constant Value

梦想的初衷 提交于 2019-12-17 17:54:33

问题


Is there a straightforward way for defining a partial specialization of a C++ template class given a numerical constant for one of the template parameters? I'm trying to create special constructors for only certain kinds of template combinations:

template <typename A, size_t B> class Example
{
    public:
        Example() { };

        A value[B];
};

template <typename A, 2> class Example
{
    public:
        Example(b1, b2) { value[0] = b1; value[1] = b2; };
};

This example won't compile, returning an error Expected identifier before numeric constant in the second definition.

I've had a look through a number of examples here and elsewhere, but most seem to revolve around specializing with a type and not with a constant.

Edit:

Looking for a way to write a conditionally used constructor, something functionally like this:

template <typename A, size_t B> class Example
{
    public:
        // Default constructor
        Example() { };

        // Specialized constructor for two values
        Example<A,2>(A b1, A b2) { value[0] = b1; value[1] = b2; };

        A foo() {
          A r;

          for (size_t i = 0; i < b; ++b)
            r += value[i];

          return r;
        }

        // Hypothetical specialized implementation
        A foo<A, 2>() {
          return value[0] + value[1];
        }

        A value[B];
};

回答1:


I think this might work:

#include <iostream>

template <typename A, size_t B>
class Example {
public:
    Example()
    {
        Construct<B>(identity<A, B>());
    }

    A foo()
    {
        return foo<B>(identity<A, B>());
    }

private:
    template <typename A, size_t B>
    struct identity {};

    template <size_t B>
    void Construct(identity<A, B> id)
    {
        for (size_t i = 0; i < B; ++i)
        {
            value[i] = 0;
        }
        std::cout << "default constructor\n";
    }

    template <size_t B>
    void Construct(identity<A, 2> id)
    {
        value[0] = 0;
        value[1] = 0;
        std::cout << "special constructor\n";
    }

    template <size_t B>
    A foo(identity<A, B> id)
    {
        A r = 0;
        for (size_t i = 0; i < B; ++i)
        {
            r += value[i];
        }
        std::cout << "default foo\n";
        return r;
    }

    template <size_t B>
    A foo(identity<A, 2> id)
    {
        std::cout << "special foo\n";
        return value[0] + value[1];
    }

    A value[B];
};

int main()
{
    Example<int, 2> example; // change the 2 to see the difference
    int n = example.foo();
    std::cin.get();
    return 0;
}

Sorry, I just copy and pasted it from my test project. It's not really "specialization" in a way, it just calls overloads to specialized functions. I'm not sure if this is what you want and imo this isn't very elegant.




回答2:


You need to put the specialization in the correct place:

template <typename A> class Example<A,2>

If you want to create a subclass:

template <typename A> class ExampleSpecialization : public Example<A,2>

The behavior for specializing on typedefs is similar to the behavior for specializing on an integer parameter.




回答3:


If memory serves, it should be more like:

template <typename A, size_t B> class Example
{
    public:
        Example() { };

        A value[B];
};

template <typename A> class Example<A, 2>
{
    public:
        Example(A b1, A b2) { value[0] = b1; value[1] = b2; };
};

I don't think this is quite allowable as-is though -- there's nothing defining the types of b1 and/or b2 in the specialized version.

Edit [based on edited question]: Yes, a template specialization produces a new type that's not really related to the base from which it's specialized. In particular, the two do not share any of the implementation. You can't (by specializing a class template) produce a single type that uses one of two different ctors, depending on the value of a non-type parameter.




回答4:


You can try something like this:

template<size_t s>
struct SizeTToType { static const size_t value = s; };

template<bool> struct StaticAssertStruct;
template<> struct StaticAssertStruct<true> {};
#define STATIC_ASSERT(val, msg) { StaticAssertStruct<((val) != 0)> ERROR_##msg; (void)ERROR_##msg;}

template <typename A, size_t B> 
class Example
{
    public:
        Example() { };
        Example(A b1){ value[0] = b1; }
        Example(A b1, A b2) { 
                STATIC_ASSERT(B >= 2, B_must_me_ge_2); 
                value[0] = b1; value[1] = b2;
        } 
        A foo() { return in_foo(SizeTToType<B>()); }
    protected:
        template<size_t C>
        A in_foo(SizeTToType<C>) {
                cout << "univ" << endl;
                A r;
                for (size_t i = 0; i < B; ++i)
                r += value[i];
                return r;
        }
        A in_foo(SizeTToType<2>){
                cout << "spec" << endl;
                return value[0] + value[1];
        }
        A value[B];
};

Working example on http://www.ideone.com/wDcL7

In templates if you are not using method it won't exists in compiled code, so this solution shouldn't make executable bigger because of ctors you can't use with some specialized class (for example Example<int, 1> should not have Example(A b1, A b2) ctor).




回答5:


If you're goal is to only have to override a few methods/constructors in your specializations then maybe consider a generic base class to hold the common implementation for all Example templates so you don't have to rewrite it in every specialization you come up with.

For example:

template < typename A, size_t B >
class ExampleGeneric {
public:

  // generic implementation of foo inherited by all Example<A,B> classes
  void foo() {
    A r;

    for (size_t i = 0; i < B; ++i)
      r += value[i];

    return r;
    }

  // generic implementation of bar inherited by all Example<A,B> classes
  void bar() {
    A r;

    for (size_t i = 0; i < B; ++i)
      r *= value[i];

    return r;
    }

  A values[B];
  };

template < typename A, size_t B >
class Example : public ExampleGeneric<A,B> {
public:
  //default to generic implementation in the general case by not overriding anything
  };

//*** specialization for 2
template < typename A >
class Example<A,2> : public ExampleGeneric<A,2>{
public:

  // has to be provided if you still want default construction
  Example() {
    }

  //extra constructor for 2 parameters
  Example( A a1, A a2 ) {
    values[0] = a1;
    values[1] = a2;
    }

  // specialization of foo
  void foo() {
    return values[0] + values[1];
    }

  // don't override bar to keep generic version
  };



回答6:


#include <iostream>

using namespace std;


template<typename _T, size_t S>
class myclass {
    _T elem[S];
public:
    myclass() {
        for (int i = 0; i < S; i++) {
            elem[i] = i;
        }
    }
    void Print() {
        for (int i = 0; i < S; i++) {
            cout << "elem[" << i << "] = " << elem[i] << endl;
        }
    }
};


int main(int argc, char **argv)
{
    myclass < int, 10 > nums;
    nums.Print();
    myclass < int, 22 > nums1;
    nums1.Print();
}

That worked on my linux machine with

g++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48) Copyright (C) 2006 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.



来源:https://stackoverflow.com/questions/4840819/c-template-specialization-with-constant-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!