MSVC:推理指向具有常量和非常量重载的成员函数的指针
MSVC: inference of pointer to member function with const and non-const overloads
这是一个最小的示例,其中 MSVC 无法决定是使用成员函数的 const 版本还是非 const 版本:
struct A {
int b() const;
int& b();
};
template <typename T, typename Ref>
void set(A&, Ref (A::*)(), T);
int main() {
auto a = A{};
set(a, &A::b, 123);
}
错误消息是
错误 C2783:"无效集(A &,引用 (__cdecl A::* )(无效),T)":无法推断出"Ref"的模板参数
GCC 和 Clang 更喜欢 non-const 方法,并且编译它没有问题。它们需要Ref (A::*)() const
来选择常量版本。
有没有办法将MSVC推向正确的方向?
https://godbolt.org/z/ejT-Ls
您可以通过强制转换函数指针来强制选择非 const 版本:
set(a, static_cast<int & (A::*)()>(&A::b), 123);
我相信这一定是MSVC中的一个错误。您有权调用函数模板,但需扣除模板参数。一个参数是指向成员函数类型的指针,因此 [temp.deduct.call]/6 适用:
当
P
是函数类型、函数指针类型或指向成员函数类型的指针时:
- 如果参数是包含一个或多个函数模板的重载集,则该参数将被视为非推导上下文。
- 如果参数是重载集(不包含函数模板),则尝试使用该集的每个成员进行试验参数推导。如果只有一个重载集成员的扣除成功,则该成员将用作扣除的参数值。如果重载集的多个成员的演绎成功,则参数将被视为非推导上下文。
在您的情况下,参数是不包含任何函数模板的重载集。因此,通过 [temp.deduct.call]/6.2,编译器应该尝试使用每个方法进行参数推导。参数推导应该只使用一种方法(非常量方法)成功,然后应该是为函数调用选择的方法。
@SoronelHaetir已经在他的回答中发布了一个解决方法。我建议您提交错误报告...
完成这项工作的另一种方法是切换set
的模板参数,并用decltype
指定第一个:
template <typename Ref, typename T>
void set(A&, Ref (A::*)(), T);
set<decltype(A{}.b())>(a, &A::b, 123);
对于不影响调用方的解决方法,可以推断类类型,并且仅在推导类型为 A 时启用该函数。
#include <type_traits>
struct A {
int b() const;
int& b();
};
template <typename T, typename Ref, typename DeducedA,
typename std::enable_if<std::is_same<DeducedA, A>::value, int>::type = 0>
void set(A&, Ref (DeducedA::*)(), T);
int main() {
auto a = A{};
set(a, &A::b, 123);
}
https://godbolt.org/z/GPxb9xaen
相关文章:
- 什么时候可以使用常量装饰调用我的重载函数?
- 如何为非常量和常量重载实现一次成员函数?
- 一种优雅或至少可行的方法,用于使用和接受具有重载方法和运算符的不同大小的文字数组常量
- 重载模板化类的[]运算符的常量版本
- 引用模板类型的赋值运算符需要非常量重载
- MSVC:推理指向具有常量和非常量重载的成员函数的指针
- 为什么 lambda 自动和参数选择常量重载?
- 非常量指针更喜欢常量 T&重载而不是常量 T*
- asio_handler_invoke常量重载
- 不能只删除方法的常量重载?
- 如何获取提供常量重载的函数类型
- 基于常量重载
- 二元运算符 + , = 使用常量重载
- 如何检查成员函数是否具有常量重载
- C++使用一个参数常量重载
- 为什么常量左值与给定 T&& 和常量 T& 重载的常量右值绑定不同?
- 指向常量重载成员函数的指针
- 常量重载和共享指针
- c++非常量-常量重载方法选择
- 如何调用非常量重载