如何键入-擦除C ++概念

时间:2019-07-14 17:01:20

标签: c++ c++20 c++-concepts

我试图实现一个小型的网络库以了解概念,并且试图找到一种定义简洁概念的方法,而不必在依赖概念上携带模板参数。例如,我有以下概念:

template <typename ValueT>
concept bool Value = true;

template <typename BufferT>
concept bool Buffer = requires(BufferT buf)
{
    { buf.Size() } -> std::size_t;
    { buf.Capacity() } -> std::size_t;
    { buf.Put(Value</* How can I resolve this to any value */>) } -> bool;
    { buf.Take(Value</* How can I resolve this to any value */>) } -> bool;
};

template <typename ReadableT>
concept bool Readable = requires(ReadableT r)
{
    { r.Read(Buffer</* How can I resolve this to any buffer */>) } -> void;
};

template <typename WritableT>
concept bool Writable = requires(WritableT w)
{
    { w.Write(Buffer</* How can I resolve this to any buffer */>) } -> void;
};

template <typename ChannelT>
concept bool Channel = requires(ChannelT chan)
{
    requires Readable<ChannelT>;
    requires Writable<ChannelT>;
};

如何在不必显式具有模板参数的情况下定义ValueBuffer概念?可能吗我会这样直观地写:

template <typename ReadableT>
concept bool Readable = requires(ReadableT r)
{
    template <typename ValueT> 
    { r.Read(Buffer<ValueT>) } -> void;
};

但是(显然)这不能编译,我无法弄清楚正确的语法。

编辑:我觉得正确的语法如下:

template <typename BufferT>
concept bool Buffer = requires(BufferT buf, Value val)
{
    { buf.Size() } -> std::size_t;
    { buf.Capacity() } -> std::size_t;
    { buf.Put(val) } -> bool;
    { buf.Take(val) } -> bool;
};

但是GCC(8.3.0)会显示以下消息:

internal compiler error: in synthesize_implicit_template_parm, at cp/parser.c:39141
    concept bool Buffer = requires(BufferT buf, Value val)
                                             ^~~~~
Please submit a full bug report, with preprocessed source if appropriate.

2 个答案:

答案 0 :(得分:1)

  

我的想法是我应该能够将任何结构,整数或字符串或任何真正位于缓冲区中的东西

这不是一个概念能够回答的问题。也不是故意的。

概念旨在约束模板。模板在某种程度上应该知道它在做什么。特定的模板实例化不适用于“任何结构,整数或字符串”;它适用于特定类型,该类型由其模板参数和依赖于它们的表达式定义。概念也是如此。

因此请考虑这样的模板:

template<typename Buff, typename Iterator>
void InsertIntoBuffer(Buff &buf, Iterator beg, Iterator ed)
{
  for(; beg != ed; ++beg)
    buf.Put(*beg);
}

此函数要施加在Buff上的约束不是不是“具有可以接受任何对象的Put函数”。实际的约束条件是“具有一个Put函数,该函数可以接受Iterator的{​​{1}}返回的值。”

因此,“投入”和“接受”不仅是对operator*的约束;它也需要知道正在“放”或“取”的东西。

换句话说,不是具有约束的类型。整个操作受到限制。

因此,您将拥有Buff的基本约束,该约束具有大小和容量。但是您还应该有一个Buffer约束,它强制要求PutBuffer可以Buffer给定类型。

类似地,Put实际上是Readable,其中提供了缓冲区类型。

答案 1 :(得分:0)

例如,您不能在类型上拥有无限的约束,例如它支持put(int)put(int*)put(int**)等。 。 (通常如何检查这种约束?)您真的不想宣称存在可以接受任何 put的模板T。 (传统的,不受约束的)语法表明它可以,几乎总是存在隐含的约束(或假设),这些隐含的约束(或假设)可以通过使用 some T实例化来打破(请考虑诸如{{1 }}。

作为序列化的实际问题,您可以仅检查void() const是否接受putintfloat,还可以与代表人const char*一起检查大概。