Boost.Iostreams 流无法转发 4 个构造函数参数

Boost.Iostreams stream can't forward 4 constructor arguments

我正在尝试使用 Boost 的 stream class 创建自定义 std::istream。它应该比手动创建 std::streambuf 更容易,但我遇到了障碍。

构造一个 stream 需要一个 source 对象。 stream 有一组 forwarding constructors 接受参数列表并将它们转发给源的构造函数——这就是我想要使用的。

// Forwarding constructors

template<typename U>
stream([const] U& u);

template<typename U1, typename U2>
stream([const] U1& u1, const U2& u2);

    ...

template<typename U1, ..., typename UN>
stream([const] U1& u1, const U2& u2, ..., const UN& uN);

Each of these members constructs an instance of stream and associates it with an instance of the Device T constructed from the given lists of arguments. The T constructors involved must take all arguments by value or const reference.

它在三个参数下效果很好:

#include <memory>
#include <boost/iostreams/stream.hpp>

namespace io = boost::iostreams;

class IoSource: public io::source {
public:
    IoSource(int foo, int bar, int baz) { }
    std::streamsize read(char *buffer, std::streamsize n) { return -1; }
};

int main() {
    auto stream = std::make_unique<io::stream<IoSource>>(1, 2, 3);
}

结果:

$ g++ -Wall -Werror -std=gnu++20 test.cpp -o /dev/null

具有四个参数的同一程序无法编译,并出现大量模板错误:

#include <memory>
#include <boost/iostreams/stream.hpp>

namespace io = boost::iostreams;

class IoSource: public io::source {
public:
    IoSource(int foo, int bar, int baz, int quux) { }
    std::streamsize read(char *buffer, std::streamsize n) { return -1; }
};

int main() {
    auto stream = std::make_unique<io::stream<IoSource>>(1, 2, 3, 4);
}

结果:

$ g++ -Wall -Werror -std=gnu++20 test.cpp -o /dev/null
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h: In instantiation of 'typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = boost::iostreams::stream<IoSource>; _Args = {int, int, int, int}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<boost::iostreams::stream<IoSource>, std::default_delete<boost::iostreams::stream<IoSource> > >]':
test.cpp:14:68:   required from here
/usr/include/c++/10/bits/unique_ptr.h:962:30: error: no matching function for call to 'boost::iostreams::stream<IoSource>::stream(int, int, int, int)'
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
                 from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
                 from /usr/include/boost/iostreams/detail/forward.hpp:22,
                 from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(U100&, const U0&, const U1&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = int; U0 = int; U1 = int; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; typename boost::disable_if<boost::is_same<U0, T> >::type = void]' (near match)
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   conversion of argument 4 would be ill-formed:
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: error: invalid conversion from 'int' to 'boost::disable_if_c<false, void>::type*' {aka 'void*'} [-fpermissive]
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                              |
      |                              int
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
                 from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
                 from /usr/include/boost/iostreams/detail/forward.hpp:22,
                 from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(const U0&, const U1&, const U2&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U0 = int; U1 = int; U2 = int; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; typename boost::disable_if<boost::is_same<U0, T> >::type = void]' (near match)
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   conversion of argument 4 would be ill-formed:
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: error: invalid conversion from 'int' to 'boost::disable_if_c<false, void>::type*' {aka 'void*'} [-fpermissive]
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                              |
      |                              int
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
                 from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
                 from /usr/include/boost/iostreams/detail/forward.hpp:22,
                 from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U100, class U0> boost::iostreams::stream<Device, Tr, Alloc>::stream(U100&, const U0&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = U100; U0 = U0; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note:   candidate expects 3 arguments, 4 provided
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
                 from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
                 from /usr/include/boost/iostreams/detail/forward.hpp:22,
                 from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U0, class U1> boost::iostreams::stream<Device, Tr, Alloc>::stream(const U0&, const U1&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U0 = U0; U1 = U1; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note:   candidate expects 3 arguments, 4 provided
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
                 from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
                 from /usr/include/boost/iostreams/detail/forward.hpp:22,
                 from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U100> boost::iostreams::stream<Device, Tr, Alloc>::stream(U100&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U100 = U100; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note:   candidate expects 2 arguments, 4 provided
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/preprocessor/tuple/elem.hpp:23,
                 from /usr/include/boost/preprocessor/repetition/enum_binary_params.hpp:19,
                 from /usr/include/boost/iostreams/detail/forward.hpp:22,
                 from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'template<class U0> boost::iostreams::stream<Device, Tr, Alloc>::stream(const U0&, typename boost::disable_if<boost::is_same<U0, T> >::type*) [with U0 = U0; Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   template argument deduction/substitution failed:
In file included from /usr/include/c++/10/memory:83,
                 from test.cpp:1:
/usr/include/c++/10/bits/unique_ptr.h:962:30: note:   candidate expects 2 arguments, 4 provided
  962 |     { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/boost/iostreams/stream.hpp:18,
                 from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(const boost::reference_wrapper<T>&, std::streamsize, std::streamsize) [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; std::streamsize = long int]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   candidate expects 3 arguments, 4 provided
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(Device&, std::streamsize, std::streamsize) [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; std::streamsize = long int]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   candidate expects 3 arguments, 4 provided
/usr/include/boost/iostreams/stream.hpp:144:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream(const Device&, std::streamsize, std::streamsize) [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>; std::streamsize = long int]'
  144 |     BOOST_IOSTREAMS_FORWARD( stream, open_impl, Device,
      |     ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/iostreams/stream.hpp:144:5: note:   candidate expects 3 arguments, 4 provided
In file included from test.cpp:3:
/usr/include/boost/iostreams/stream.hpp:143:5: note: candidate: 'boost::iostreams::stream<Device, Tr, Alloc>::stream() [with Device = IoSource; Tr = std::char_traits<char>; Alloc = std::allocator<char>]'
  143 |     stream() { }
      |     ^~~~~~
/usr/include/boost/iostreams/stream.hpp:143:5: note:   candidate expects 0 arguments, 4 provided

我做错了什么?我相信我已经将其缩小到核心问题。这似乎纯粹是参数数量的问题;它们是值还是 const 引用并不重要,它们的类型也不重要。

Boost 不使用 variadic templates 和完美转发来转发参数。它使用预处理器宏来模拟可变参数,具有最大数量的宏:

boost/iostreams/detail/config/limits.hpp
#ifndef BOOST_IOSTREAMS_MAX_FORWARDING_ARITY
# define BOOST_IOSTREAMS_MAX_FORWARDING_ARITY 3
#endif

在包含 Boost 之前定义上面的宏可以增加参数的数量 headers:

#define BOOST_IOSTREAMS_MAX_FORWARDING_ARITY 4
#include <boost/iostreams/stream.hpp>