Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Initialise class template depending on another template

This is what I have tried thus far:

Here is my code:

template<typename T1>
struct Foo 
{
    template<typename T2>
    using MyPair = std::pair<T1, T2>;
    using MyPairs = std::vector<MyPair>;

    Foo() 
    {
        //if T1 is an int, then I want T2 to be a double.
        //if T1 is a float, then I want T2 to be an int.
    }
};

I would like T2 to be a double if T1 is an int. I would like T2 to be an int if T1 is a float.

How am I able to do this?

like image 417
expl0it3r Avatar asked May 24 '26 16:05

expl0it3r


2 Answers

There are many ways to solve this.

Option - I

The simple way is to use std::conditional to define the alias for T2. In which if the T1 == int the T2 will be double, all other types the T2 will be an alias for int. (See a demo)

#include <utility>
#include <type_traits> // std::conditional, std::is_same 

template<typename T1>
struct Foo /* final */
{
   static_assert(std::is_same<T1, int>::value || std::is_same<T1, float>::value, "NOT A VALID TYPE T1");

   using T2 = typename std::conditional<std::is_same<T1, int>::value, double, int>::type;

   using MyPair = std::pair<T1, T2>;
};

If you want to restrict the instantiation of the class for other types, provide a conditional instantiation of the class or a static_assert.


Option - II

You can use traits specialization to define the T2 type. (See a demo)

#include <vector>
#include <utility>

// base template!
template <typename T1> struct helper_traits;

template <> //  when helper_traits<`T1 == int`>  then `T2 == double`
struct helper_traits<int> final {  using T2 = double; };

template <> //  when helper_traits<`T1 == float`>  then `T2 == int`
struct helper_traits<float>  final { using T2 = int; };

template<typename T1>
struct Foo /* final */
{
   using T2 = typename helper_traits<T1>::T2; // will select the proper one!

   using MyPair = std::pair<T1, T2>;
};

Option - III

In c++17, using the if constexpr you could decide which type to be returned in the function, and use it to know the type of T2 as follows: (See a demo)

#include <type_traits> // std::is_same_v

template<typename T1>
struct Foo /* final */
{
   template<typename Type>
   static constexpr auto typeHelper() noexcept
   {
      if constexpr (std::is_same_v<Type, int>)
         return double{};
      else if constexpr (std::is_same_v<Type, float>) 
         return int{};
   }
   using T2 = decltype(Foo<T1>::typeHelper<T1>()); // will select the proper one!

   using MyPair = std::pair<T1, T2>;
};
like image 70
JeJo Avatar answered May 27 '26 05:05

JeJo


This solution hasn't a nested template but works as you expect:

template<typename T1,
         class = std::enable_if_t<std::is_same<T1, int>::value || std::is_same<T1, float>::value>>
struct Foo {
    typedef typename std::conditional<std::is_same<T1, int>::value, double, int>::type T2;
    using MyPair = std::pair<T1, T2>;
    using MyPairs = std::vector<MyPair>;
    MyPairs elements;
    Foo() {
        //if T1 is an int, then I want T2 to be a double.
        //if T1 is a float, then I want T2 to be an int.
        std::cout << typeid(T2).name() << std::endl;
    }
};

int main()
{
    Foo<int> f;

    f.elements.push_back (std::make_pair(1.4, 2.3));

    std::cout << f.elements[0].first << ", " << f.elements[0].second << std::endl;
    
    return 0;
}

Output:

d
1, 2.3
like image 20
Manuel Avatar answered May 27 '26 05:05

Manuel