代码之家  ›  专栏  ›  技术社区  ›  Antoine Morrier

函数参数的MSVC和constexpr?

  •  9
  • Antoine Morrier  · 技术社区  · 6 年前

    template<size_t n>
    struct N {
        static constexpr size_t v = n;
    };
    
    template<size_t n>
    constexpr bool operator<(N<n>, size_t n2) {
        return n < n2;
    }
    
    template<typename N>
    constexpr void foo(N v) {
        static_assert(v < 5);
    }
    
    int main()
    {
        foo(N<3>{});
        return 0;
    }
    

    v < 5 不是常量表达式。我可以理解MSVC为什么这么认为,但我认为这是错误的,而clang/gcc是正确的。是MSVC的bug吗?

    3 回复  |  直到 6 年前
        1
  •  5
  •   Rakete1111    6 年前

    是的,MSVC错了。

    代码格式良好似乎有悖常理,因为 v

    那为什么允许呢?首先,非正式地考虑一下,一个表达式不是一个常量表达式,如果它的计算结果是一个本身不是常量表达式的glvalue,或者是一个在封闭表达式之外开始使用的变量( [expr.const]p2.7 ).

    其次, operator< constexpr .

    现在,发生的是 v < 5

    我们有:

    1. 五级 常量表达式 操作员<
    2. 这两个参数被复制(都是文本,没有一个计算为非constexpr对象)
    3. n2 它的生命开始于 五级 是个字面意思
    4. n 是非类型模板参数,因此在常量表达式中可用
    5. 最后, n < n2

    所有这些都不会违反 [expr.const]p2 ,因此得到的表达式实际上是一个常量表达式,用作 static_assert

    这些类型的表达式称为 converted constant expressions

    struct Foo {
      constexpr operator bool() { return true; }
    };
    
    int main() {
      Foo f;
      static_assert(f);
    }
    
        2
  •  1
  •   Shafik Yaghmour    6 年前

    MSVC在这里是不正确的,让我们从代码的简化版本开始:

    struct N {
        static constexpr size_t v = 0;
    };
    
    constexpr 
      bool operator<(N n1, size_t n2) {
        return n1.v < n2;
    }
    
      void foo(N v) {
        static_assert(v < 5, ""); // C++11 does not allow terse form
    }
    

    static_assert 首先,我们是否违反了常量表达式的任何规则?如果我们看看 [expr.const]p2.2 :

    对文本类或constexpr的constexpr构造函数以外的函数的调用 功能[注:过载分辨率(13.3)和通常的结束注一样使用];

    operator< 是constexpr函数和的复制构造函数 N 是文本类的constexpr构造函数。

    移动到 操作员< n1.v < n2 [expr.const]p2.9 :


    -一种整型或枚举型的glvalue,它引用一个非易失性常量对象,该对象具有一个预先初始化、用常量表达式初始化或
    这样一个对象的子对象,或
    -一种文本类型的glvalue,它引用一个非易失性的临时对象,该对象的生存期尚未结束

    我们在这里也很好。在最初的示例中,我们引用了一个模板非类型参数,该参数在常量表达式中可用,因此同样的推理也适用于该情况。的两个操作数 < 是可用的常量表达式。

    我们也可以看到 MSVC still treats the simplified case as ill-formed 即使叮当声和海湾合作委员会接受它。

        3
  •  -1
  •   Oliv    6 年前

    如果您声明:

    template<size_t n>
    struct N {
      int i;
      static constexpr size_t v = n;
     };
    

    演示 here .

    v operator< . 这样的副本是对 v

    对你来说 N 是一个空类。我不认为标准规定了这样一个类的复制构造函数是否应该访问对象的内存 1 core language issue 1701 ). 因此编译器表现出一种依赖于是否访问空类对象的内存的行为。

    compiler explorer link

    所以我认为所有的编译器都是对的。


    1 reinterpret_cast