在模板中转换成员函数指针

时间:2018-09-13 13:41:59

标签: c++ templates language-lawyer member-function-pointers

假设我有以下两个类:

template<typename T>
struct Base
{
    void foo();
};

struct Derived : Base<Derived> {};

我可以这样做:

void (Derived::*thing)() = &Derived::foo; 

而且编译器很高兴(正如我期望的那样)。

当我将其放在两个级别的模板中时,它突然爆炸:

template<typename T, T thing>
struct bar {};

template<typename T>
void foo()
{
    bar<void (T::*)(),&T::foo>{};
}

int main()
{
    foo<Derived>();  // ERROR
    foo<Base<Derived>>(); // Works fine
}

此操作失败,并显示以下信息:

non-type template argument of type 'void (Base<Derived>::*)()' cannot be converted to a value of type 'void (Derived::*)()'

godbolt

为什么简单的案例会起作用,而更复杂的案例会失败?我相信这与this问题有关,但并不完全确定。...

3 个答案:

答案 0 :(得分:6)

@YSC固定了&Derived::foo;的类型。既然您想知道为什么要执行这种隐式转换...

void (Derived::*thing)() = &Derived::foo; 

...飞行正常,但不在模板中,原因如下:

  

[临时参数非类型]

     

2非类型模板参数的模板参数应为   转换为模板参数类型的常量表达式。

     

[expr.const]

     

4类型T的转换常量表达式是一个表达式,   隐式转换为T类型,其中转换后的表达式为   常量表达式,并且隐式转换序列仅包含

     
      
  • [...]
  •   

我省略的列表不包含pointer to member conversions。因此,使该模板参数对于您指定的参数无效。


一个简单的解决方法是使用decltype(&T::foo)而不是void (T::*)()作为类型参数。这是格式正确的替换:

bar<decltype(&T::foo), &T::foo>{};

是否可以接受,当然取决于您的用例,超出了MCVE的范围。

答案 1 :(得分:4)

这是因为&Derived::foo实际上是类型void (Base<Derived>::*)()

  

[expr.unary]/3

     

一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定ID。如果操作数是一个合格的ID,其命名为类型T的某些C类的非静态或变体成员m,则结果的类型为``指向类型T的C类的成员的指针'',并且是一个指定C的prvalue :: m。

请注意“某些类型T的C类成员m” ...措辞糟糕。

答案 2 :(得分:0)

请注意,即使没有模板,这也会是一个错误,例如,如果您的类不是模板:

struct Base {
    void foo();
};

struct Derived : Base {};

您仍然无法执行foo<Base>()https://ideone.com/MQE0ff


一个可能的替代解决方案将首先涉及到简化,而不是采用2个模板参数,我将让bar使用auto模板参数类型:

template<auto thing>
struct bar{};

接下来,我们需要实现is_specialization_of

template<template<typename...> class T, typename U>
struct is_specialization_of : std::false_type {};

template<template<typename...> class T, typename... Ts> 
struct is_specialization_of<T, T<Ts...>> : std::true_type {};

现在,我们可以重写foo以使用is_specialization_of,我们可以确定是否通过了Base专业化认证或另一个类(我们假定它是从Base专业化衍生而来的) )

template<typename T>
void foo()
{
    conditional_t<is_specialization_of<Base, T>::value, bar<&T::foo>, bar<&Base<T>::foo>>{};
}

我已经稍微扩展了您的示例,以在thing中实际调用bar,并在其他方面采纳了我的建议。您可以在此处查看:https://coliru.stacked-crooked.com/a/2a33b8bd38896ff5

相关问题