构造函数无法从概念中推断模板参数
Constructor can't infer template argument from concept
为什么在这种情况下构造函数无法从概念中推断出模板参数?也许还有一种方法可以在序列化程序概念中不指定具体的模板类型。
#include <span>
template <class impl_t>
concept stream = requires(impl_t &impl) {
{ impl.write(std::span<const std::byte>{}) };
};
struct any_t {};
template <class impl_t, class stream_t>
concept serializer = requires(impl_t &impl, const stream_t &stream, any_t &any) {
::stream<stream_t>;
{ impl.serialize(any) } -> std::same_as<stream_t>;
};
class memory_stream {
public:
void write(std::span<const std::byte> data) noexcept {
}
};
template <stream stream_t>
class json_serializer {
public:
template <typename T>
stream_t serialize(const T &value) {
return {};
}
};
template <stream stream_t, serializer<stream_t> serializer_t>
class client {
public:
explicit client(serializer_t &serializer) noexcept {
}
};
int main() {
json_serializer<memory_stream> s;
client c(s);
}
这不是 C++20 中推导的工作方式。
对于class模板参数推导(CTAD),client
class模板需要推导两种类型(stream_t
和serializer_t
)和您拥有的构造函数仅使用其中一种类型,因此该语言不知道另一种可能来自何处。
您可以通过为 json_serializer
:
提供推导指南来帮助解决这个问题
template <stream S>
client(json_serializer<S>) -> client<S, json_serializer<S>>;
现在 client c(s);
可以工作了,因为您告诉它流类型在此上下文中的来源。如果您为序列化程序关联的 what 流添加一些关联类型,则这可以推广到任何类型的序列化程序。
猜测:
template <typename S>
using stream_for = decltype(std::declval<S&>().serialize(42));
template <typename Serializer>
client(Serializer) -> client<stream_for<Serializer>, Serializer>;
如果 serializer
有一个一元概念而不是二元概念,这会有所帮助。
请注意,在您的概念定义中,要求:
::stream<stream_t>;
不检查 stream_t
是否满足 stream
概念。它检查这是一个有效的表达式。无论是否满足该概念(false
与 true
一样有效)。
这需要是:
requires ::stream<stream_t>;
或者,甚至更好:
template <class Serializer, class Stream>
concept serializer_for =
stream<Stream>
&& requires (Serializer& s, any_t a) {
{ s.serialize(a) } -> std::same_as<Stream>;
};
您可以将 class 重写为
template <stream stream_t, template <typename> typename S>
requires serializer<S<stream_t>,stream_t>
class client{
explicit client(S<stream_t> &serializer){}
}
或者您可以添加推演指南
template<typename Stream, template<typename> typename Serializer>
client(Serializer<Stream>) -> client<Stream,Serializer<Stream>>;
@Barry 也指出,您需要修改约束定义。
例如:
template <class T>
concept stream = requires(T &t) {
{ t.write(std::span<const std::byte>{}) };
};
struct any_t {};
template <class T, class S>
concept serializer = stream<S> && requires(T t, any_t &any) {
{ t.serialize(any) } -> std::same_as<S>;
};
为什么在这种情况下构造函数无法从概念中推断出模板参数?也许还有一种方法可以在序列化程序概念中不指定具体的模板类型。
#include <span>
template <class impl_t>
concept stream = requires(impl_t &impl) {
{ impl.write(std::span<const std::byte>{}) };
};
struct any_t {};
template <class impl_t, class stream_t>
concept serializer = requires(impl_t &impl, const stream_t &stream, any_t &any) {
::stream<stream_t>;
{ impl.serialize(any) } -> std::same_as<stream_t>;
};
class memory_stream {
public:
void write(std::span<const std::byte> data) noexcept {
}
};
template <stream stream_t>
class json_serializer {
public:
template <typename T>
stream_t serialize(const T &value) {
return {};
}
};
template <stream stream_t, serializer<stream_t> serializer_t>
class client {
public:
explicit client(serializer_t &serializer) noexcept {
}
};
int main() {
json_serializer<memory_stream> s;
client c(s);
}
这不是 C++20 中推导的工作方式。
对于class模板参数推导(CTAD),client
class模板需要推导两种类型(stream_t
和serializer_t
)和您拥有的构造函数仅使用其中一种类型,因此该语言不知道另一种可能来自何处。
您可以通过为 json_serializer
:
template <stream S>
client(json_serializer<S>) -> client<S, json_serializer<S>>;
现在 client c(s);
可以工作了,因为您告诉它流类型在此上下文中的来源。如果您为序列化程序关联的 what 流添加一些关联类型,则这可以推广到任何类型的序列化程序。
猜测:
template <typename S>
using stream_for = decltype(std::declval<S&>().serialize(42));
template <typename Serializer>
client(Serializer) -> client<stream_for<Serializer>, Serializer>;
如果 serializer
有一个一元概念而不是二元概念,这会有所帮助。
请注意,在您的概念定义中,要求:
::stream<stream_t>;
不检查 stream_t
是否满足 stream
概念。它检查这是一个有效的表达式。无论是否满足该概念(false
与 true
一样有效)。
这需要是:
requires ::stream<stream_t>;
或者,甚至更好:
template <class Serializer, class Stream>
concept serializer_for =
stream<Stream>
&& requires (Serializer& s, any_t a) {
{ s.serialize(a) } -> std::same_as<Stream>;
};
您可以将 class 重写为
template <stream stream_t, template <typename> typename S>
requires serializer<S<stream_t>,stream_t>
class client{
explicit client(S<stream_t> &serializer){}
}
或者您可以添加推演指南
template<typename Stream, template<typename> typename Serializer>
client(Serializer<Stream>) -> client<Stream,Serializer<Stream>>;
@Barry 也指出,您需要修改约束定义。 例如:
template <class T>
concept stream = requires(T &t) {
{ t.write(std::span<const std::byte>{}) };
};
struct any_t {};
template <class T, class S>
concept serializer = stream<S> && requires(T t, any_t &any) {
{ t.serialize(any) } -> std::same_as<S>;
};