从指向成员的指针的模板推导,其中至少有一个指向成员的指针是已知的

Template deduction from pointer-to-members, where at least one pointer-to-member is known

本文关键字:成员 指针 有一个      更新时间:2023-10-16

考虑一个结构,其中包含接受指向成员的指针函数的静态方法模板。请注意,当方法的其中一个参数是实际的指向成员的指针函数时,可以推导出两个模板参数,无论另一个参数是否为nullptr

请参阅以下代码下面的问题:

struct Checker
{
template <typename T, typename V>
static void Check(
V(T::*getter)(),
void(T::*setter)(V)
);
template <typename T, typename V>
static void CheckDefault(
V(T::*getter)() = nullptr,
void(T::*setter)(V) = nullptr
);
};
struct Test
{
int Value();
void Value(int);
int Getter();
void Setter(int);
};
Checker::CheckDefault(&Test::Value);           //1
Checker::CheckDefault(&Test::Value, nullptr);  //2
Checker::Check(&Test::Value, nullptr);         //3
Checker::CheckDefault(&Test::Getter);          //4
Checker::CheckDefault(&Test::Getter, nullptr); //5
Checker::Check(&Test::Getter, nullptr);        //6
  • 为什么&Test::Value的正确过载可以在 1 中确定,而在 2 和 3 中却不能确定?
  • 为什么 1 和 4 能够推断出正确的类型名称,而 2、3、5 和 6 却不能?

>编辑我希望能够调用方法,并将两个参数中的至少一个设置为实际的指向成员函数,从而导致推导成功。这样:

Checker::Check(&Test::Value, &Test::Value); // Both getter and setter
Checker::Check(&Test::Value, nullptr); // Only getter
Checker::Check(nullptr, &Test::Value); // Only setter
<小时 />

编辑

@Oliv解释为什么它不能按我的预期工作,在例外答案中的讨论为我指明了解决特定问题的正确方向。

我最终使用了转发器,正如@Ben Voigt所建议的那样。像这样:

template <typename T, typename V>
using GetterT = V(T::*)();
template <typename T, typename V>
using SetterT = void(T::*)(V);
template <typename T, typename V>
void Checker::CheckGetterAndSetter(
GetterT<T, V> getter,
SetterT<T, V> setter
)
{
// Do stuff with getter and setter.
}
template <typename T, typename V>
void Checker::CheckGetter(
GetterT<T, V> getter
)
{
SetterT<T, V> null = nullptr;
return CheckGetterAndSetter(getter, null);
}
template <typename T, typename V>
void Checker::CheckSetter(
SetterT<T, V> setter
)
{
GetterT<T, V> null = nullptr;
return CheckGetterAndSetter(null, setter);
}

TL;DR 它之所以成功,是因为默认参数不用于模板参数推断。在其他情况下(2,3,5,6),它失败了,因为nullptr_t没有T(U::*)(V)的形式(甚至没有T*)。

让我们举一个更简单的例子:

template<class T>
void f(T,T*=nullptr);
int main(){
f(10,nullptr);// (1) error
f(10);// (2) OK
}

对于(1)编译器认为它应该能够从第一个参数和第二个参数中推断出T,因为形式为TT*的参数是免赔额。由于nullptr_t不是T*的形状,因此编译失败。

对于 (2),编译器仅对函数的第一个参数执行模板参数推导,因为默认参数在模板参数推导中不起作用。因此,它毫不含糊地推断出Tint。然后当调用该函数时,nullptr将被转换为int*