为什么模板专业化在GCC中不起作用

时间:2019-05-29 13:44:56

标签: c++ templates metaprogramming

我想编写类型列表方法以与微控制器GPIO配合使用。

我想创建GPIO列表并仅选择特定端口的引脚。 因此,GetPinWithPort模板具有专门检查提供的类型的模板。

template <typename... Ts>
struct tlist
{
    using type = tlist;
};

template <typename T> class debug_t;

#define MAKE_PORT(NAME, ID)\
    class NAME\
    {\
        public:\
        static void Set(uint32_t v) { };\
        static void Reset(uint32_t v) { };\
        enum { id = ID };\
    };

MAKE_PORT(Porta, 'A');
MAKE_PORT(Portb, 'B');

template <class PORT, uint8_t PIN>
class TPin
{
public:
    static void Set() { PORT::Set(1 << PIN); }
    static void Reset() { PORT::Reset(1 << PIN); }

    typedef PORT port;
    enum { pin = PIN };
};

template <class TPort, class T>
struct GetPinWithPort {
    using type = tlist<>;
};

template <typename TPort, uint32_t N>
struct GetPinWithPort<TPort, TPin<TPort, N>>
{
    using type = TPin<TPort, N>;
};

int main()
{

    using pina = GetPinWithPort<Porta, TPin<Porta, 1> >::type;

    // std::cout << typeid(pina).name() << std::endl;  //Visual Studio gives: class TPin<class Porta,1>
    debug_t<pina> d; //gcc output: tlist<>

}

Visual Studio提供了预期的结果。但海湾合作委员会-空列表。 怎么了?

2 个答案:

答案 0 :(得分:2)

应该是

template <typename TPort, uint8_t N>  // or auto
struct GetPinWithPort<TPort, TPin<TPort, N>>

不是

template <typename TPort, uint32_t N>
struct GetPinWithPort<TPort, TPin<TPort, N>>

因为(我不是语言律师,这就是我的理解方式):

template <class PORT, uint8_t PIN>
class TPin {}
// and 
using pina = GetPinWithPort<Porta, TPin<Porta, 1> >::type;

在专业化中,gcc必须在以下选项之间进行选择:

class T

TPin<TPort, uint32_t>

及其类型为:

TPin<Porta, 1>

所以gcc可能会将TPin<Porta, 1>解析为TPin<Porta, uint8_t>,然后使专业化失败。

答案 1 :(得分:1)

在此行using pina = GetPinWithPort<Porta, TPin<Porta, 1> >::type; 1具有int类型而不是uint32_t。因此,您实例化了GetPinWithPort<Porta, int>(非专业定义),而不是GetPinWithPort<Porta, uint32_t>

以下是专业化将正确的模板参数传递给TPin的外观:

template <typename TPort, uint8_t N>
struct GetPinWithPort<TPort, TPin<TPort, N>>
{
    using type = TPin<TPort, N>;
};

这里是应如何使用它:

using pina = GetPinWithPort<Porta, TPin<Porta, static_cast<uint8_t>(1)> >::type;

这样做的原因是C ++在使用模板类型方面非常严格:允许非常有限的类型转换。