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

问题描述 投票:2回答:1

考虑一个带有静态方法模板的结构,它接受指向成员的函数。请注意,当方法的一个参数是实际的指向成员的函数时,无论另一个参数是否为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);
}
c++
1个回答
1
投票

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*

© www.soinside.com 2019 - 2024. All rights reserved.