views:

987

answers:

11

I need a C++ template that, given a type and an object of that type, it can make a decision based on whether the type is an integer or not, while being able to access the actual objects. I tried this

template <typename T, T &N>
struct C {
    enum { Value = 0 };
};

template <int &N>
struct C<int, N> {
    enum { Value = N };
};

but it doesn't work. Is there any way I can achieve something similar?

Edit

What I was trying to achieve was something like this, that would happen at compile time:

if (type is int) {
    return IntWrapper<int_value>
else {
    return type
}

You can actually pass pointers or references to objects in a template instantiation, like so:

struct X {
    static const int Value = 5;
};

template <X *x>
struct C {
    static const int Value = (*x).Value; 
};

X x;

std::cout << C<&x>::Value << std::endl; // prints 5

but apparently all this accomplishes is to initialize the template by inferring x's type, and x also needs to be declared globally. No use for what I'm trying to do, which I think is not possible after all at compile time.

+7  A: 

What you are attempting to do isn't valid C++ templating. You can't use arbitrary objects as template parameters, all you can use are types, integral literals and in certain specialised cases string literals.

workmad3
And boolean values as well
Benoît
Don't know why anyone downvoted you, but I hope some more will help get you on top...
Pieter
+3  A: 

template <typename T> struct A
{
    enum { Value = false };
};
template <> struct A<int>
{
    enum { Value = true };
};

How about this then:


template <typename T> struct A
{
    T value_;
    A() : value() {}
    enum { is_int = false };
};
template <> struct A<int>
{
    int value_;
    explicit A( int v ) : value_( v ) {}
    enum { is_int = true };
};
Nikolai N Fetissov
This would work, but I also need the actual value if it is an integer.
drill3r
+2  A: 

You can do it like this:

template<typename T, int val>
struct Test
{
    enum {Value = 0};
};

template <int val>
struct Test<int, val>
{
    enum {Value = val};
};




int main(int argc,char *argv[])
{
    int v = Test<int,1>::Value;
}
Naveen
+3  A: 

Addition to the other posts: You don't need to use the enum {}-hack any more:

template<typename T, int val>
struct Test {
    static const int Value = 0;
};

template <int val>
struct Test<int, val> {
    static const int Value = val;
};


int main(int argc,char *argv[]) {
    const int v = Test<int,1>::Value;
}
Dario
A: 

simple fix to your code - loose the reference:

template <typename T, T N>
struct C {
    enum { Value = 0 };
};

template <int N>
struct C<int, N> {
    enum { Value = N };
};

using reference in a template argument is meaningless anyway because you're not actually passing the argument anywhere.

shoosh
This pretty much only works with integers. Try it with float.
Nikolai N Fetissov
+4  A: 

Perhaps a simple overloaded template method works in your case?

template<typename T>
void doSomething(const T& x)
{
    // ...
}
void doSomething(int x)
{
    // ...
}
SebastianK
+2  A: 

I need a C++ template that, given a type and an object of that type, it can make a decision based on whether the type is an integer or not, while being able to access the actual objects.

You can make decisions based on the type being an integer or not, the problem is it's impossible to declare a template with an object of any type. So the question on how to decide wether a type is an integer is moot.

Note that in all answers your original template is neatly changed to

template < typename T, int >
class C {};

instead of your

template< typename T, T >
class C {};

But while C<int, 5> is a perfectly valid declaration, this is not the case for an arbitrary type T, case in point C<float, 5.> will give a compiler error.

Can you post what you're trying to achieve exactly?

And for the record, if the second template argument is always an int, and you simply want to take its value if the type is an integer type, and 0 otherwhise, you can simply do:

#include <limits>

template< typename T, int N >
class C {
    static const int Value = (std::numeric_limits<T>::is_integer) ? N : 0;
};
Pieter
A: 

Check out Alexandrescu's Modern C++ Design. I believe chapter 2 has a correct example of what you want to do.

ceretullis
+5  A: 

Unless i misunderstand you, what you want is impossible. In your example you show an invalid use of a pointer template parameter.

template <X *x>
struct C {
    static const int Value = (*x).Value; 
};

That's not valid, since (*x).Value must be a constant expression for it to be able to initialize Value. Sure Value within class X would be fine as a constant expression when used as X::Value instead. But this time, it's not since it involves a pointer (references are equally invalid in constant expressions).

To sum up, you can't do this:

Magic<T, someT>::type

And expect ::type to be T if T isn't int, and IntWrapper<someT> otherwise, since T can only be an enumeration, integer, pointer or reference type. And in the latter two cases, you won't get at the "value" of anything pointed to by the pointer or referred to by the reference at compile time. If you are satisfied with that, it's easy to solve your problem and i'm not going to show you how (i suspect you already know how).

I think you have driven yourself into a situation where solving your problem has become impossible to do with the rules as given. Drive back some steps and show us the real problem you are trying to solve, when the matter still allows to solve things.

Johannes Schaub - litb
A: 

Template specialization can be achieved like this (code taken from www.cplusplus.com):

// template specialization
#include <iostream>
using namespace std;

// class template:
template <class T>
class mycontainer {
    T element;
  public:
    mycontainer (T arg) {element=arg;}
    T increase () {return ++element;}
};

// class template specialization:
template <>
class mycontainer <char> {
    char element;
  public:
    mycontainer (char arg) {element=arg;}
    char uppercase ()
    {
      if ((element>='a')&&(element<='z'))
      element+='A'-'a';
      return element;
    }
};

int main () {
  mycontainer<int> myint (7);
  mycontainer<char> mychar ('j');
  cout << myint.increase() << endl;
  cout << mychar.uppercase() << endl;
  return 0;
}

In your case you would have to replace the char by what you want in the class template specialization. Now, I am not really sure what you are trying to accomplish but I hope the example above is a good indicator to how you can do some template specialization.

Partial
A: 

What I was trying to achieve was something like this, that would happen at compile time:

if (type is int) {
    return IntWrapper<int_value>
else {
    return type
}

I'm not sure why you aren't using IntWrapper to begin with. Where does the need come from to wrap a compile-time integer constant into an IntWrapper, if it is int?

Otherwise it looks a bit that you are trying to instantiate templates with data that is only available at run-time.

UncleBens