Assigning default value to a template parameter based on a previous template parameter

Viewed 81

I have a program like: (please ignore how nonsensical it looks, it just a reproducible example )

using MyUInt = unsigned int;
using MyLInt = long int;

template<class T1, class T2>
T1 fn(const T2& arg){
    return T1(arg);
}

int main() {
    std::cout << fn<int, MyUInt>(1);
    std::cout << fn<double, MyLInt>(1);

    return 0;
}

is it possible to assign a default value to T2 based on T1 so I don't have to declare it manually? What are the alternatives?

I'm looking for something like

using MyUInt = unsigned int;
using MyLInt = long int;

template<class T1, class T2 = typename std::is_same<T1, int>::value ? MyUInt : MyLInt>
T1 fn(const T2& arg){
    return T1(arg);
}

int main() {
    std::cout << fn<int>(1);
    std::cout << fn<double>(1);

    return 0;
}

I'm using C++17.

1 Answers

You can use template specialization to define a helper type to handle the mapping between types, eg:

using MyUInt = unsigned int;
using MyLInt = long int;

template<class T>
struct myStruct {
    using type = MyLInt;
};

template<>
struct myStruct<int> {
    using type = MyUInt;
};

// define other mappings as needed...

template<class T>
using myStruct_t = typename myStruct<T>::type;

template<class T1, class T2 = myStruct_t<T1>>
T1 fn(const T2& arg){
    return T1(arg);
}

int main() {
    std::cout << fn<int>(1);
    std::cout << fn<double>(1);
    return 0;
} 

Online Demo


UPDATE: apparently, the above will not work, since arg is in a deduced context, so T2 will never use its default value, and will always be the type that is actually passed in, which in this example is int.

So, I would suggest a different approach - swap the meaning of the template arguments so that arg is deduced from the caller's input, and the return type is deduced by the helper based on the input type, eg:

using MyUInt = unsigned int;
using MyLInt = long int;

template<class T>
struct myStruct {
};

template<>
struct myStruct<MyLInt> {
    using type = double;
};

template<>
struct myStruct<MyUInt> {
    using type = int;
};

// define other mappings as needed...

template<class T>
using myStruct_t = typename myStruct<T>::type;

template<class T, class Ret = myStruct_t<T>>
Ret fn(const T& arg){
    return Ret(arg);
}

int main() {
    std::cout << fn(MyUInt(1));
    std::cout << fn(MyLInt(1));

    // this also works
    std::cout << fn<MyUInt>(1);
    std::cout << fn<MyLInt>(1);

    return 0;
} 

Online Demo

Related