转换模板中的成员函数指针
Converting member function pointers in templates
假设我有以下两个类:
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::*)()'
为什么简单的案例有效而复杂的案例失败?我相信这与 this 问题有关,但我不完全确定....
那是因为 &Derived::foo
实际上是类型 void (Base<Derived>::*)()
:
[expr.unary]/3
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static or variant member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m.
注意“某些 class C 类型 T 的成员 m”... 糟糕的措辞。
确定了 &Derived::foo;
的类型。既然您想知道为什么要进行这种隐式转换...
void (Derived::*thing)() = &Derived::foo;
...正常飞但在模板中不飞,原因如下:
[temp.arg.nontype]
2 A template-argument for a non-type template-parameter shall be a
converted constant expression of the type of the template-parameter.
[expr.const]
4 A converted constant expression of type T is an expression,
implicitly converted to type T, where the converted expression is a
constant expression and the implicit conversion sequence contains only
- [...]
我省略的列表不包含pointer to member conversions。因此使该模板参数对于您指定的参数无效。
一个简单的修复方法是使用 decltype(&T::foo)
而不是 void (T::*)()
作为类型参数。这是一个格式正确的替换:
bar<decltype(&T::foo), &T::foo>{};
是否可以接受,当然取决于您的用例,超出了 MCVE 的范围。
请注意,即使没有模板,这也将是一个错误,例如,如果您的 classes 不是模板:
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
专业化或另一个 class (我们假设它来自Base
专业化。)
template<typename T>
void foo()
{
conditional_t<is_specialization_of<Base, T>::value, bar<&T::foo>, bar<&Base<T>::foo>>{};
}
我稍微扩展了您的示例以在 bar
中实际调用 thing
并在其他方面采纳了我的建议。您可以在这里查看:https://coliru.stacked-crooked.com/a/2a33b8bd38896ff5
假设我有以下两个类:
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::*)()'
为什么简单的案例有效而复杂的案例失败?我相信这与 this 问题有关,但我不完全确定....
那是因为 &Derived::foo
实际上是类型 void (Base<Derived>::*)()
:
[expr.unary]/3
The result of the unary & operator is a pointer to its operand. The operand shall be an lvalue or a qualified-id. If the operand is a qualified-id naming a non-static or variant member m of some class C with type T, the result has type “pointer to member of class C of type T” and is a prvalue designating C::m.
注意“某些 class C 类型 T 的成员 m”... 糟糕的措辞。
&Derived::foo;
的类型。既然您想知道为什么要进行这种隐式转换...
void (Derived::*thing)() = &Derived::foo;
...正常飞但在模板中不飞,原因如下:
[temp.arg.nontype]
2 A template-argument for a non-type template-parameter shall be a converted constant expression of the type of the template-parameter.
[expr.const]
4 A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only
- [...]
我省略的列表不包含pointer to member conversions。因此使该模板参数对于您指定的参数无效。
一个简单的修复方法是使用 decltype(&T::foo)
而不是 void (T::*)()
作为类型参数。这是一个格式正确的替换:
bar<decltype(&T::foo), &T::foo>{};
是否可以接受,当然取决于您的用例,超出了 MCVE 的范围。
请注意,即使没有模板,这也将是一个错误,例如,如果您的 classes 不是模板:
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
专业化或另一个 class (我们假设它来自Base
专业化。)
template<typename T>
void foo()
{
conditional_t<is_specialization_of<Base, T>::value, bar<&T::foo>, bar<&Base<T>::foo>>{};
}
我稍微扩展了您的示例以在 bar
中实际调用 thing
并在其他方面采纳了我的建议。您可以在这里查看:https://coliru.stacked-crooked.com/a/2a33b8bd38896ff5