如何将类型转换为模板成员函数参数?

时间:2019-05-01 00:56:25

标签: c++ casting overloading member-functions

我实现了类模板writerwriter具有模板成员变量s_s_的类型为Streamwriter期望Stream有一个成员函数,该成员函数可以使用参数const char* bufsize_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标志设置为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,我需要知道第二个参数类型。

所以我实现了一些参数类型提取器:

// 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成员函数已重载的情况下,发生编译错误。

正在运行的演示:https://wandbox.org/permlink/TDPlQ3nXzIKjSlhY

为了获得特定的成员函数重载,我需要了解完整的成员函数指针类型。但是,这是不可预测的。

write()

有什么好方法可以知道尺寸类型,或在没有杂物的情况下抑制警告?

1 个答案:

答案 0 :(得分:0)

在第二个版本中获取写函数的地址时,它不知道要从哪个重载获取地址。您需要充分限制模板参数,以免重载不明确:

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 *),或者只需要用户代码中的特定签名即可。