【问题标题】:Mixing delete keyword with concepts and constraints将删除关键字与概念和约束混合
【发布时间】:2021-12-05 20:24:28
【问题描述】:

In my question我收到了一个适合我的答案,但我不明白它是如何工作的。

特别是,我不明白 delete 关键字和概念如何消除 operator<< 的重载。

(我将逐个粘贴accepted answer中的代码重构版本。)

enum class LogLevel
{
    info,
    warning,
    error
};

template<typename T>
concept HasLogMethodReturningReferenceToSelf = requires(T v)
{
    {
        v.log(LogLevel{})
        } -> std::convertible_to<T&>;
};

所以,这里我们定义一个概念,检查一个类型是否有方法log(),它以LogLevel为参数,返回convertible以引用self。

然后我们删除operator&lt;&lt; 重载(阻止隐式函数生成和显式重载),这些重载在&lt;&lt; 的左侧具有满足HasLogMethodReturningReferenceToSelf 的类型和在&lt;&lt; 的右侧的类型不是std::string的类型:

template<HasLogMethodReturningReferenceToSelf T, class U>
requires(!std::convertible_to<U, std::string>) auto operator<<(T, U) = delete;
  1. 我不明白什么时候会删除重载?在某些特定类型T 的概念“实例化”期间?因为,不是针对满足标准的每种类型(它会破坏代码库?)?

因为后面定义了另一个概念,它检查基本类型的输出流运算符重载:

template<typename T>
concept HasOutputStreamOperatorOverloadsForBasicTypes =
    requires(T v, unsigned unsigned_, int int_, float float_, unsigned char unsigned_char_, char char_)
{
    {
        v << "string literal" //
          << unsigned_        //
          << int_             //
          << float_           //
          << unsigned_char_   //
          << char_            //
        } -> std::convertible_to<T&>;
};

最后,我们定义一个Loggable 概念:

template<typename T>
concept Loggable = HasLogMethodReturningReferenceToSelf<T> && HasOutputStreamOperatorOverloadsForBasicTypes<T>;

通常,我会定义一个大的Loggable 概念:

template<typename T>
concept Loggable = requires(T v)
{
    {
        v.log(LogLevel{})
        } -> std::convertible_to<T&>;
    {
        v << "string literal" //
          << unsigned_        //
          << int_             //
          << float_           //
          << unsigned_char_   //
          << char_            //
        } -> std::convertible_to<T&>;
};

然后以某种方式限制隐式转换。我猜答案的作者不得不将那些requires 表达式拆分为两个,因为否则,当我使用后者Loggable 定义然后delete 定义后的重载:

template<Loggable T, class U>
requires(!std::convertible_to<U, std::string>) auto operator<<(T, U) = delete;

我得到编译错误:

致命错误:递归模板实例化超出最大深度 1024

  1. 为什么?

【问题讨论】:

  • 你有这个答案有效的例子吗?因为除非我弄错了,HasOutputStreamOperatorOverloadsForBasicTypes 要求基本类型工作确实会破坏它:gcc.godbolt.org/z/z4esEc1vx
  • @Frank 您添加的示例确实有效,因为您的Logger1 缺少重载,这就是我们想要捕获的。见:gcc.godbolt.org/z/6G8x9rG8G
  • 哦,我明白了。有问题的类型列表是如此随意,以至于我认为您想要“隐式转换为 std::string + 没有其他隐式转换 + 一些基于每个记录器定义的任意类型列表”。我没有意识到 double short long long 等...在您的设计中是从不可记录类型。

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


【解决方案1】:

我不明白什么时候会删除重载?期间 某些特定类型 T 的概念的“实例化”?因为不是 对于满足标准的每种类型(它会破坏 代码库?)?

当我们检查 v &lt;&lt; unsigned_ 是否在 requires 子句中格式正确时,如果您的 Logger 类没有 operator&lt;&lt;unsigned 重载,则将选择在概念之前定义的全局 operator&lt;&lt;,因为U 会直接实例化为unsigned 并且没有隐式转换,所以优先级更高。

并且因为全局operator&lt;&lt;被删除了,所以v &lt;&lt; unsigned_是病态的,不满足约束。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-18
    • 1970-01-01
    相关资源
    最近更新 更多