tags:

views:

179

answers:

5

Hi, I have a template class in C++ (somewhat simplified):

template<typename T>
struct C
{
  T member;
  void set(const &T x) { member = x; }
  void set(int x) { member = x; }
};

As you can see the set() function can be called either with the type T, or with an int. This works fine unless T is an int, in which case I get an ambiguous conversion error. I understand why this is happening, but is there any way to implement what I want?

+2  A: 

Provide a specialisation for int:

template<>
struct C<int>
{
  int member;
  void set(int x) { member = x };
};

?

jon hanson
Nice - I forgot about these.
Preet Sangha
+1  A: 

One way around this would be to provide a specialisation of the template for int that only has one set function. Otherwise you might want to have a look at the Boost libraries if something like enable_if in their template meta programming code would allow you to turn on the function set(int x)only when T is not of type int.

Timo Geusch
A: 

could you cast the call to either int or const int in the calling code?

Preet Sangha
A: 
Phil Nash
Yeah, so, ok, I should have given more details. I want to make a class that pretends to be a number. There are many subtle traps here, for example you can get silent double->int conversions which can be very bad. In my example member is always a number (float, double, high precision float etc). template<typename U> void setInternal(const U }This is a bit too promiscuous, I was trying it but it can lead to some hard to detect bugs.
uekstrom
A: 

It appears that you want to make the set method available only if the argument is T or int. However, if you just specialize C for int, all the implicit conversions still happen, just as if you didn't attempt to treat int type in a special way at all.

If you really want to disable implicit conversions, one way is with boost::enable_if - make the method only available, if the criteria are satisfied.

#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>

template<typename T>
struct C
{
    T member;
    template <class U>
    typename boost::enable_if_c<
        boost::is_same<T, U>::value || boost::is_same<U, int>::value
    >::type
    set(const U& x) { member = x; }
};


int main()
{
    C<int> i;
    i.set(3.14); //error
    i.set(10);   //OK
    i.set('a');  //error

    C<double> d;
    d.set(3.14);  //OK
    d.set(3.14f); //error
    d.set(10);    //OK
    d.set('a');   //error
}

To achieve the same result without boost::enable_if (or implementing it yourself), you might also need to make all unwanted versions of set private (C++0x also allows to delete these overloads).

template<typename T>
struct C
{
    T member;

    void set(const T& x) { member = x; }
    void set(int x) { member = x; }
private:
    template <class U>
    void set(const U&);
};

template <>
struct C<int>
{
    int member;
    void set(int x) { member = x; }
private:
    template <class U>
    void set(const U&);
};


int main()
{
    C<int> i;
    i.set(3.14); //error
    i.set(10);   //OK
    i.set('a');  //error

    C<double> d;
    d.set(3.14);  //OK
    d.set(3.14f); //error
    d.set(10);    //OK
    d.set('a');   //error
}

However, making this exception for int seems rather arbitrary to me. Conversions from int might not be particularly safe, either, e.g int->float might lose precision for large values, int->short / int->char / int->unsigned might overflow.

UncleBens