代码之家  ›  专栏  ›  技术社区  ›  drilow

概念约束模板函数调用的模糊性

  •  3
  • drilow  · 技术社区  · 6 年前

    template <typename T> inline constexpr bool C1 = true;    
    template <typename T> inline constexpr bool C2 = true;
    
    template <typename T> requires C1<T> && C2<T> 
    constexpr int foo() { return 0; }
    
    template <typename T> requires C1<T> 
    constexpr int foo() { return 1; }
    
    constexpr int bar() {
        return foo<int>();
    }
    

    这是电话吗 foo<int>() 不明确,或者约束 C1<T> && C2<T> 包含 C1<T> ?

    0 回复  |  直到 6 年前
        1
  •  23
  •   Barry    6 年前

    对。 仅限 概念可以包含在内。呼叫 foo<int>

    C1 C2 两个都是 concept inline constexpr bool s、 然后是 foo() 那就回来了 0 至少会像 foo() 1 ,以及 foo<内部> 将有效并返回 . 这是一个喜欢使用概念作为约束而不是任意布尔常量表达式的原因。


    这种差异的原因(概念包含,任意表达式不包含)最好用 Semantic constraint matching for concepts ,这是值得一读的全文(我不会在这里重复所有的论点)。但以论文中的一个例子:

    namespace X {
      template<C1 T> void foo(T);
      template<typename T> concept Fooable = requires (T t) { foo(t); };
    }
    namespace Y {
      template<C2 T> void foo(T);
      template<typename T> concept Fooable = requires (T t) { foo(t); };
    }
    

    X::Fooable Y::Fooable 尽管它们的含义完全不同(由于在不同的名称空间中定义)。这种附带的等价是有问题的:一个重载集的函数受这两个概念的约束将是不明确的。

    namespace Z {
      template<C3 T> void foo(T);
      template<C3 T> void bar(T);
      template<typename T> concept Fooable = requires (T t) {
        foo(t);
        bar(t);
      };
    }
    

    十: :可食 , ,和 Z::Fooable Z: :可食 . 这几乎肯定不是程序员想要的。


    标准参考文献

    包容原则是正确的 [temp.constr.order]/1.2

    原子约束 包含另一个原子约束 当且仅当 A B类 使用中描述的规则是相同的[原子结构温度].

    [temp.constr.atomic] :

    是由表达式构成的 E 以及显示在中的模板参数的映射 电子 到包含受约束实体的模板参数的模板参数,称为参数映射([施工温度]). [注: 原子约束通过约束规范化形成。 AND 表达式或逻辑表达式 OR 表情。 [尾注]

    完全相同的 如果它们是由相同的 表达 根据中描述的表达式规则,参数映射的目标是等价的[超链接温度].

    这里的关键是原子约束 形成 . 这是关键所在。在 [temp.constr.normal] :

    这个 范式 一个 表达 是一个约束,定义如下:

    • 表达式E1 | | E2的范式是E1和E2的范式的析取。
    • 表达式E1&E2的范式是E1和E2的范式的结合。
    • id表达式 表格C<A 1 ,一个 2 ,…,A >,其中C表示一个概念,是 1 ,一个 2 ,…,A 对于C的各个模板参数在每个原子约束中的参数映射。如果任何这样的替换导致无效的类型或表达式,则程序是格式错误的;不需要诊断。[ ... ]
    • 任何其他表达式的标准形式 电子 是原子约束,其表达式为

    第一次过载 foo ,约束为 C1<T> && C2<T> 为了规范化它,我们得到了 C1<T> 1 C2<T> 1 ,约束为 2 这是它自己的范式。

    使原子约束相同的规则是,它们必须由相同的约束组成 (源代码级构造)。而这两个函数都有一个使用令牌序列的原子约束 C1<T> ,它们不一样 在源代码中。

    因此,这些下标表明,事实上,它们不是相同的原子约束。 C1<T> 1 2 . 规则不是令牌等价!所以第一个 ,反之亦然。

    因此,模棱两可。

    另一方面,如果我们有:

    template <typename T> concept D1 = true;    
    template <typename T> concept D2 = true;
    
    template <typename T> requires D1<T> && D2<T> 
    constexpr int quux() { return 0; }
    
    template <typename T> requires D1<T> 
    constexpr int quux() { return 1; }
    

    D1<T> && D2<T> . 第三颗子弹给了我们 D1<T> D2<T> true 1 是的 2 . 同样,下标表示 哪一个 是的 正在引用。

    第二个函数的约束是 D1<T> 是的 1 .

    现在, 是的 确实与 1 ,因此这些约束被认为是相同的。因此, 包涵体 D1<T> ,和 quux<int>() 是一个明确的呼叫返回 0 .