Как ввести тип как параметр функции-члена шаблона? - PullRequest
0 голосов
/ 01 мая 2019

Я реализовал шаблон класса writer.writer имеет переменную-член шаблона s_.Тип s_ равен Stream.writer ожидает, что Stream имеет функцию-член, которая может вызываться с параметрами const char* buf и size_t len.

Вот первая версия writer:

// clang++ -Wconversion test.cpp

#include <cstddef>
#include <ostream>

template <class F> struct size_arg_type;

template <typename Ret, typename Cls, typename T1, typename T2>
struct size_arg_type<Ret (Cls::*)(T1, T2)> {
    using type = T2;
};

template <typename Stream>
struct writer {
    writer(Stream& s):s_(s) {}

    void write(const char* buf, size_t len) {
        // The type of 2nd parameter depends on Stream
        s_.write(buf, len);
    }

    Stream& s_;
};

struct user_stream1 {
    void write(const char*, size_t) {}
};

struct user_stream2 {
    void write(const char*, std::streamsize) {}
};

struct user_stream3 {
    void write(const char*, size_t) {}
    void write() {}
};

#include <sstream>

int main() {
    {   // size_type is size_t
        user_stream1 s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
    {   // size_type is std::streamsize
        user_stream2 s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
#if 1
    {   // size_type is size_t but has overloaded member function
        user_stream3 s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
#endif
    {   // size_type is std::streamsize return type is std::ostream&
        std::stringstream s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
}

Запуск демо: https://wandbox.org/permlink/JtEHDG3plWxe4vwB

Если я установил -Wconversion flags на clang ++, я получил следующие предупреждения:

clang++ -std=c++17 -Wconversion test.cpp 
test.cpp:18:23: warning: implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to
      'std::streamsize' (aka 'long') [-Wsign-conversion]
        s_.write(buf, len);
           ~~~~~      ^~~
test.cpp:50:11: note: in instantiation of member function 'writer<user_stream2>::write' requested here
        w.write(buf, sizeof(buf));
          ^
test.cpp:18:23: warning: implicit conversion changes signedness: 'size_t' (aka 'unsigned long') to
      'std::streamsize' (aka 'long') [-Wsign-conversion]
        s_.write(buf, len);
           ~~~~~      ^~~
test.cpp:64:11: note: in instantiation of member function 'writer<std::__cxx11::basic_stringstream<char>
      >::write' requested here
        w.write(buf, sizeof(buf));
          ^
2 warnings generated.

Compilation finished at Wed May  1 09:37:37

Я попытался найти способ подавления предупреждений без прагмы.Я придумал подход static_cast.Чтобы сделать static_cast, мне нужно знать 2-й тип параметра.

Поэтому я реализовал некоторый экстрактор типа параметра:

// clang++ -std=c++17 -Wconversion test.cpp

#include <cstddef>
#include <ostream>

template <class F> struct size_arg_type;

template <typename Ret, typename Cls, typename T1, typename T2>
struct size_arg_type<Ret (Cls::*)(T1, T2)> {
    using type = T2;
};

template <typename Stream>
struct writer {
    writer(Stream& s):s_(s) {}

    void write(const char* buf, size_t len) {
        write_impl(&Stream::write, buf, len);
    }

    template <typename Write>
    void write_impl(Write, const char* buf, size_t len)
    {
        s_.write(buf, static_cast<typename size_arg_type<Write>::type>(len));
    }

    Stream& s_;
};

struct user_stream1 {
    void write(const char*, size_t) {}
};

struct user_stream2 {
    void write(const char*, std::streamsize) {}
};

struct user_stream3 {
    void write(const char*, size_t) {}
    void write() {}
};

#include <sstream>

int main() {
    {   // size_type is size_t
        user_stream1 s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
    {   // size_type is std::streamsize
        user_stream2 s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
#if 1
    {   // size_type is size_t but has overloaded member function
        user_stream3 s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
#endif
    {   // size_type is std::streamsize return type is std::ostream&
        std::stringstream s;
        writer w(s);
        char buf[] = "123";
        w.write(buf, sizeof(buf));
    }
}

Он работает, как я и ожидал.Однако в случае user_stream3, в котором перегружена функция-член write(), возникает ошибка компиляции.

Запуск демонстрации: https://wandbox.org/permlink/TDPlQ3nXzIKjSlhY

Для получения определенной перегрузки функций-членов Iнужно знать тип указателя на функцию-член.Однако это непредсказуемо.

    clang++ -std=c++17 -Wconversion test.cpp 
    test.cpp:18:9: error: no matching member function for call to 'write_impl'
            write_impl(&Stream::write, buf, len);
            ^~~~~~~~~~
    test.cpp:63:11: note: in instantiation of member function 'writer<user_stream3>::write' requested here
            w.write(buf, sizeof(buf));
              ^
    test.cpp:22:10: note: candidate template ignored: couldn't infer template argument 'Write'
        void write_impl(Write, const char* buf, size_t len)
             ^
    1 error generated.

    Compilation exited abnormally with code 1 at Wed May  1 09:48:42

Есть ли какой-нибудь хороший способ узнать тип размера или подавить предупреждение без прагмы?

1 Ответ

0 голосов
/ 01 мая 2019

При получении адреса функции записи во второй версии он не знает, для какой перегрузки взять адрес.Вам необходимо в достаточной степени ограничить параметры вашего шаблона, чтобы перегрузки не были неоднозначными:

template <typename Stream>
struct writer {
    writer(Stream& s):s_(s) {}

    void write(const char* buf, size_t len) {
        write_impl(&Stream::write, buf, len);
    }

    template <typename Ret, typename Cls, typename T1, typename T2>
    void write_impl(Ret (Cls::*)(T1, T2), const char* buf, size_t len)
    {
        s_.write(buf, static_cast<T2>(len));
    }

    Stream& s_;
};

https://wandbox.org/permlink/7Qb6xAoUQPRz3o2u

Однако, это все равно не удастся, если существует несколько 2-аргументов write функции.В этом случае вам придется либо найти больше ограничений (например, первый аргумент должен быть const char *), либо просто потребовать конкретную подпись из кода пользователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...