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

Dar*_*bik 6 c++ templates member-function-pointers language-lawyer

假设我有以下两个类:

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

struct Derived : Base<Derived> {};
Run Code Online (Sandbox Code Playgroud)

我可以做这个:

void (Derived::*thing)() = &Derived::foo; 
Run Code Online (Sandbox Code Playgroud)

编译器很高兴(正如我所料).

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

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
}
Run Code Online (Sandbox Code Playgroud)

这失败了:

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

godbolt

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

Sto*_*ica 6

@YSC钉了类型&Derived::foo;.既然你想知道为什么这个隐式转换......

void (Derived::*thing)() = &Derived::foo; 
Run Code Online (Sandbox Code Playgroud)

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

[temp.arg.nontype]

2非类型模板参数的template-argument必须是template-parameter类型的转换常量表达式.

[expr.const]

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

  • [...]

我省略的列表不包含指向成员转换的指​​针.因此,使该模板参数对您指定的参数无效.


一个简单的修复方法是使用decltype(&T::foo)而不是void (T::*)()类型参数.这是一个结构良好的替代品:

bar<decltype(&T::foo), &T::foo>{};
Run Code Online (Sandbox Code Playgroud)

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