如何定义依赖于模板参数的typedef的typedef

时间:2019-03-06 11:30:16

标签: c++ templates c++14

我想制作一个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 个答案:

答案 0 :(得分:10)

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.

答案 1 :(得分:1)

我对这个问题的五分钱。

#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