简体   繁体   中英

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
    }
};

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM