43

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];
};
tadman
  • 208,517
  • 23
  • 234
  • 262

6 Answers6

23

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.

Foo Bah
  • 25,660
  • 5
  • 55
  • 79
  • This seems to create an entirely unrelated class where `value` is not defined. – tadman Jan 30 '11 at 02:50
  • You can write template class ExampleSpecialization : public Example -- answer updated – Foo Bah Jan 30 '11 at 02:52
  • I guess I've kind of run up against a wall. The parent class will introduce instances of itself when generic methods are called, but the subclasses will emit incompatible subclasses. I guess there's no way to write a conditional constructor and/or override specific methods in specific cases? – tadman Jan 30 '11 at 02:55
  • you need to give a little more color about what you want to do – Foo Bah Jan 30 '11 at 02:59
10

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.

Marlon
  • 19,924
  • 12
  • 70
  • 101
  • Using that approach I get an error `Explicit specialization in non-namespace scope` – tadman Jan 30 '11 at 03:50
  • I guess thats because I'm using VS .. http://stackoverflow.com/questions/3052579/explicit-specialization-in-non-namespace-scope – Marlon Jan 30 '11 at 04:01
5

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.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • You need to specify two template arguments – Foo Bah Jan 30 '11 at 02:49
  • Yeah, I kind of bashed around with different options but it comes up with errors, declares an unrelated class, or a subclass which doesn't quite augment the main class properly. Missing argument types are my bad cut and paste job. – tadman Jan 30 '11 at 02:59
3

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
  };
MerickOWA
  • 7,453
  • 1
  • 35
  • 56
3

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).

Pawel Zubrycki
  • 2,703
  • 17
  • 26
  • That's an interesting approach, but the trick I was looking for was declaring the full class plus template parameters in the constructor definitions. – tadman Jan 31 '11 at 04:26
  • And how it is different than adding `STATIC_ASSERT` to such constructor? You can't use it in code, so the code doesn't exist. – Pawel Zubrycki Jan 31 '11 at 13:59
0
#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.

Coding Mash
  • 3,338
  • 5
  • 24
  • 45
NoName
  • 1