Сбой при устранении неоднозначности перегруженного метода - PullRequest
1 голос
/ 25 марта 2019

То, что следует, сокращено от системы, которая хранит указатели на методы вместе с типами их параметров.Пользователь просто предоставляет type::method, а механизм шаблона делает все остальное.Когда метод перегружен, пользователь должен предоставить подпись нужного метода.

Это работало очень хорошо, пока мы не попробовали его с некоторыми boost::asio вещами.Следующий код демонстрирует проблему:

#include <boost/asio.hpp>

using namespace boost::asio::ip;
using namespace boost::system;

template <typename TT, typename MFP, MFP> struct OpM;

template <typename TR, typename TT, typename ... Ts, TR (TT::*f)(Ts...)>
struct OpM<TT, TR (TT::*)(Ts...), f> {};

using sig = error_code (tcp::socket::*)(const tcp::endpoint&, error_code&);

struct RM {
  template <class C, typename R, typename ... Ps>
  RM(R (C::*)(Ps...)) {
    typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
  }
} MRegisterer(static_cast<sig>(&tcp::socket::connect));

g ++ 8.3 не удается скомпилировать с сообщением:

g++ -std=c++17 -c connect.cpp 
connect.cpp: In instantiation of 'RM::RM(R (C::*)(Ps ...)) [with C = boost::asio::basic_stream_socket<boost::asio::ip::tcp>; R = boost::system::error_code; Ps = {const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&}]':
connect.cpp:19:40:   required from here
connect.cpp:17:46: error: conversion from 'boost::system::error_code (boost::asio::basic_socket<boost::asio::ip::tcp>::*)(const endpoint_type&, boost::system::error_code&)' {aka 'boost::system::error_code (boost::asio::basic_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)'} to 'boost::system::error_code (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)' in a converted constant expression
     typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
                                                            ^~
connect.cpp:17:46: error: could not convert template argument '& boost::asio::basic_socket<boost::asio::ip::tcp>::connect' from '<unresolved overloaded function type>' to 'boost::system::error_code (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const boost::asio::ip::basic_endpoint<boost::asio::ip::tcp>&, boost::system::error_code&)'

Странно, что сообщение об ошибке ссылается на ошибку преобразования из ... boost::asio::basic_socket ...на ... boost::asio::basic_stream_socket ... (и что-то похожее для параметра endpoint).

Я предоставляю полный тип метода, в RM, кажется, работает нормально, но когда метод передается OpM очевидно, что компилятор запутался.

Что не так?

Для полноты картины это вывод clang ++ 8.0:

~/bin/clang++ -std=c++17 -c connect.cpp 
connect.cpp:17:37: error: conversion from '<overloaded function type>' to
      'boost::system::error_code
      (boost::asio::basic_stream_socket<boost::asio::ip::tcp>::*)(const
      boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &,
      boost::system::error_code &)' is not allowed in a converted constant
      expression
    typedef OpM<C, R (C::*)(Ps...), &tcp::socket::connect> OP;
                                    ^~~~~~~~~~~~~~~~~~~~~
connect.cpp:19:3: note: in instantiation of function template specialization
      'RM::RM<boost::asio::basic_stream_socket<boost::asio::ip::tcp>,
      boost::system::error_code, const
      boost::asio::ip::basic_endpoint<boost::asio::ip::tcp> &,
      boost::system::error_code &>' requested here
} MRegisterer(static_cast<sig>(&tcp::socket::connect));
  ^
1 error generated.

1 Ответ

4 голосов
/ 25 марта 2019

Вот краткое воспроизведение той же проблемы без Boost.Asio или даже с перегруженными функциями:

struct B {
    void foo();
};

struct D : B { };

template <typename T, T> struct X { };
using T = X<void (D::*)(), &D::foo>; // error

Проблема в том, что тип &D::foo, несмотря на то, что написано D::, на самом деле void (B::*)().И этот тип не является неявно конвертируемым в void (D::*)().

Приятная вещь для вас заключается в том, что, поскольку вы используете C ++ 17, вам на самом деле не нужно проходить через эту явную типизацию rigamarole, выможно просто написать:

template <auto F> struct X { };
using T = X<&D::foo>; // fine

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

...