Как определить typedef, который зависит от typedef аргумента шаблона

#c #templates #c 14

#c #шаблоны #c 14

Вопрос:

Я хотел бы создать a, typedef который зависит от существования typedef в аргументе шаблона:

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

Есть ли способ заставить его работать, используя std::conditional или что-то еще в C 14?

Комментарии:

1. Используйте SFINAE

Ответ №1:

Есть, с небольшим количеством 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;
};
  

При этом используется стандартное соглашение, в котором мета-функции шаблона предоставляют имя элемента type , но вы можете адаптировать его для ваших собственных нужд в именовании. Здесь используется единственный бит, отличный от C 14, std::void_t но эквивалентная вещь может быть реализована в C 14 (ее просто нельзя поместить в пространство имен std ). Вы используете его в своем классе следующим образом:

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

Здесь происходит то, что компилятор выполняет сопоставление с шаблоном при выборе специализации шаблона. Если у класса C есть член type , то предоставленная нами частичная специализация будет считаться более специализированной и как таковая выбрана. В противном случае (если замена завершается неудачей при проверке специализации), всегда можно вернуться к основному шаблону.

Живая программа, с которой можно повозиться.

Комментарии:

1. Кстати, поздравляю со 100 кб

2. @SombreroChicken — Большое вам спасибо! Теперь мне просто нужно решить, стоило ли это такой цены, в основном моего здравомыслия

3. @SombreroChicken — Я углубился в C только потому, что кто-то однажды ошибся в SO. Затем просто свернулся с ним.

4. Хм, мог бы быть я

5. @StoryTeller запоздалый вопрос, зачем нам typename = void в качестве третьего аргумента шаблона в первой функции?

Ответ №2:

Мои пять центов на этот вопрос.

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

Живая программа