Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define a typedef which is dependent on a template argument's typedef

I'd like to make a typedef which is dependent on the existence of a typedef in a template argument:

struct foo
{
    using MyType = int;
};

template <typename T = foo>
struct bar
{
    // Pseudo code
    #if T::MyType is defined
        using MyType = T::MyType;
    #else
        using MyType = double;
    #endif
};

Is there a way to get it to work using std::conditional or something else in C++14?

like image 503
Shmoopy Avatar asked Mar 06 '19 11:03

Shmoopy


2 Answers

There is, with a bit of sfinae.

template<class, typename Fallback, typename = void>
struct type_or_default {
    using type = Fallback;
};

template<class C, typename F>
struct type_or_default<C, F, std::void_t<typename C::type>> {
    using type = typename C::type;
};

This uses the standard convention where template meta-functions expose a member name type, but you can adapt it for your own naming needs. The only non-C++14 bit here is std::void_t, but an equivalent thing can be implemented in C++14 (it just can't be put into namespace std). You use it in your class like this:

template <typename T = foo>
struct bar
{
    using type = typename type_or_default<T, double>::type;
};

What happens here is that the compiler does its pattern matching when choosing a template specialization. If the class C has a member type, then the partial specialization we provided will be considered more specialized, and as such chosen. Otherwise (if substitution fails when checking the specialization), the primary template is always there to fall back to.

Live program to tinker with.

like image 61
StoryTeller - Unslander Monica Avatar answered Nov 15 '22 09:11

StoryTeller - Unslander Monica


My five cents to this question.

#include <type_traits>

template <typename T, typename DefaultType>
struct CalculateMyType {
    template <typename C>
    static typename C::MyType test(typename C::MyType*);

    template <typename>
    static DefaultType test(...);
    typedef decltype(test<T>(nullptr)) MyType;
};


struct foo
{
    using MyType = int;
};


template <typename T = foo>
struct bar
{
    using MyType = typename CalculateMyType<T, double>::MyType;
};


struct baz
{
};

struct quux
{
    using MyType = float;
};


#include <iostream>
#include <typeinfo>

template <typename>
struct TypeToStr;

template<> struct TypeToStr<double> { const char * name = "double"; };
template<> struct TypeToStr<float> { const char * name = "float"; };
template<> struct TypeToStr<int> { const char * name = "int"; };

int main() {

    std::cout << "bar<foo>::MyType = " << TypeToStr<bar<foo>::MyType>().name << std::endl;
    std::cout << "bar<baz>::MyType = " << TypeToStr<bar<baz>::MyType>().name << std::endl;
    std::cout << "bar<quux>::MyType = " << TypeToStr<bar<quux>::MyType>().name << std::endl;
}

Live program

like image 24
user2807083 Avatar answered Nov 15 '22 09:11

user2807083