MSVC: вывод указателя на функцию-член с постоянными и неконстантными перегрузками - PullRequest
0 голосов
/ 30 апреля 2019

Это минимальный пример, когда MSVC не может решить, использовать ли постоянную или неконстантную версию функции-члена:

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: 'void set (A &, Ref (__cdecl A :: *) (void), T)': не удалось вывести аргумент шаблона для 'Ref'

GCC и Clang предпочитают неконстантный метод и скомпилируют его без проблем. Им потребуется Ref (A::*)() const для выбора версии const.

Есть ли способ подтолкнуть MSVC в правильном направлении?

https://godbolt.org/z/ejT-Ls

Ответы [ 3 ]

3 голосов
/ 30 апреля 2019

Вы можете принудительно выбрать неконстантную версию, приведя указатель функции:

set(a, static_cast<int & (A::*)()>(&A::b), 123);
1 голос
/ 30 апреля 2019

Я считаю, что это должно быть ошибкой в ​​MSVC. У вас есть вызов шаблона функции, который подлежит вычету аргумента шаблона. Один параметр имеет указатель на тип функции-члена, поэтому применяется [temp.deduct.call] / 6 :

Когда P является типом функции, типом указателя функции или указателем на тип функции-члена:

  • Если аргумент является набором перегрузки, содержащим один или несколько шаблонов функций, этот параметр обрабатывается как не выводимый контекст.
  • Если аргумент является набором перегрузки (не содержащим шаблонов функций), попытка выведения пробного аргумента предпринимается с использованием каждого из членов набора. Если вывод выполняется только для одного из членов набора перегрузки, этот элемент используется в качестве значения аргумента для вывода. Если вывод выполняется успешно для более чем одного члена набора перегрузки, параметр обрабатывается как невыгруженный контекст.

В вашем случае аргумент является набором перегрузки, который не содержит шаблонов функций. Таким образом, с помощью [temp.deduct.call] /6.2 компилятор должен попытаться вывести аргумент для каждого из методов. Вывод аргумента должен завершиться успешно только с одним методом (неконстантным), который затем должен быть выбран для вызова функции.

@ SoronelHaetir уже опубликовал обходной путь в своем ответе. Я бы предложил вам подать отчет об ошибке & hellip;

0 голосов
/ 30 апреля 2019

Еще один способ сделать эту работу - переключить параметры шаблона set и указать первый с помощью decltype:

template <typename Ref, typename T>
void set(A&, Ref (A::*)(), T);

set<decltype(A{}.b())>(a, &A::b, 123);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...