Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

make a variadic constructor for signed and unsigned variables using enable_if

I want to make a constructor for a class, using any integral type, but differentiate between signed and unsigned. I don't want this to be a template on the class itself. The following is not working. Visual Studio is just saying no arguments will match.

class Thing{
public:
    template<typename Integral>
    Thing(
        typename std::enable_if<
            std::is_integral<Integral>::value &&
            !std::is_same<Integral,bool>::value &&
            std::is_signed<Integral>::value
            ,Integral
        >::type num
    ){
        //constructor using signed variable as input
    }
    template<typename Integral>
    Thing(
        typename std::enable_if<
            std::is_integral<Integral>::value &&
            !std::is_same<Integral,bool>::value &&
            !std::is_signed<Integral>::value//notice this is different
            ,Integral
        >::type num
    ){
        //constructor using unsigned variable as input
    }
};
like image 837
tomatopipps Avatar asked Feb 01 '16 18:02

tomatopipps


2 Answers

We need to move the SFINAE into the template. If we use

class Thing{
public:
    template<typename Integral, typename std::enable_if<
            std::is_integral<Integral>::value &&
            !std::is_same<Integral,bool>::value &&
            std::is_signed<Integral>::value
            ,Integral>::type* = nullptr> // will fail if type does not exist
    Thing(Integral i)
//        ^ use Integral type here
    {
        std::cout << "signed\n";
    }
    template<typename Integral, typename std::enable_if<
            std::is_integral<Integral>::value &&
            !std::is_same<Integral,bool>::value &&
            !std::is_signed<Integral>::value//notice this is different
            ,Integral>::type* = nullptr>
    Thing(Integral i)
    {
        std::cout << "unsigned\n";
    }
};

int main()
{
    int a = 10;
    Thing b(a);
    unsigned int c = 10;
    Thing d(c);
}

We get

signed
unsigned

Live Example

I also had to make the constructors public as they were private by default.

like image 178
NathanOliver Avatar answered Oct 22 '22 07:10

NathanOliver


The problem is that the type appears in a non-deduced context, so the compiler cannot deduce it from something like std::is_integral<Integral>::value. Try this instead:

#include <iostream>
#include <type_traits>

class Thing{
public:
    template<typename Integral>
    Thing(Integral num,
        typename std::enable_if<
            std::is_integral<Integral>::value &&
            !std::is_same<Integral,bool>::value &&
            std::is_signed<Integral>::value
            ,Integral
        >::type* = nullptr
    ){
        std::cout << "signed\n";
        //constructor using signed variable as input
    }

    template<typename Integral>
    Thing(Integral num,
        typename std::enable_if<
            std::is_integral<Integral>::value &&
            !std::is_same<Integral,bool>::value &&
            !std::is_signed<Integral>::value//notice this is different
            ,Integral
        >::type* = nullptr
    ){
        std::cout << "unsigned\n";
        //constructor using unsigned variable as input
    }
};

int main()
{
    int x{};
    unsigned int y{};
    Thing thing1(x);
    Thing thing2(y);
}

Live on Coliru

Side note: make your constructors public as otherwise you cannot instantiate your objects.

like image 3
vsoftco Avatar answered Oct 22 '22 07:10

vsoftco