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

Я хотел бы сделать 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?

Ответы [ 2 ]

10 голосов
/ 06 марта 2019

Есть, с небольшим количеством 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 голос
/ 06 марта 2019

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

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

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

...