【问题标题】:inline constexpr function definition legal or not? gcc (ok) vs clang (error)内联 constexpr 函数定义是否合法? gcc (ok) vs clang (error)
【发布时间】:2012-12-04 03:23:13
【问题描述】:

我当前的程序被clang拒绝,但用gcc编译得很好。它归结为以下简化示例:

struct A {
  static constexpr inline int one();
};

inline constexpr int A::one() { return 1; }

int main() {
  return 0;
}

g++ 4.7.2 编译它没有错误 (g++ -std=c++11 -Wall -g -o main example.cpp)。 clang++ 3.1 拒绝它:

$ clang++ -std=c++11 -Wall -g -o main example.cpp 
example.cpp:6:25: error: conflicting types for 'one'
inline constexpr int A::one() { return 1; }
                        ^
example.cpp:3:31: note: previous declaration is here
  static constexpr inline int one();
                              ^
1 error generated.

我敢打赌 gcc 是对的,而 clang 是错的?程序应该是合法的 C++11。

有趣的旁注。如果 one 在结构中实现,clang 不再抱怨:

struct A {
  static constexpr inline int one() { return 1; }
}

gcc 也接受这个变体。据我了解,根据标准,两个版本应该是相同的。这是一个clang错误还是我错过了什么?

【问题讨论】:

  • 另外,请注意constexpr functions and constexpr constructors are implicitly inline
  • 有趣,我不知道。不过,删除内联不会改变任何事情。 clang 仍然失败并出现相同的错误(类型冲突)。

标签: c++ c++11 clang language-lawyer


【解决方案1】:

这是一个 Clang 错误(在 Clang 3.2 中修复)。问题在于,在确定函数的重新声明是否与先前的声明匹配时,Clang 没有正确处理隐式 constness 的影响。考虑:

struct A {
  int f();                  // #1
  constexpr int f() const;  // #2 (const is implicit in C++11 and can be omitted)
  static constexpr int g(); // #3
};

int A::f() { return 1; }           // #4, matches #1
constexpr int A::f() { return 1; } // #5, matches #2, implicitly const
constexpr int A::g() { return 1; } // #6, matches #3, not implicitly const

当将类外声明#5 与A 的成员匹配时,编译器有一个问题:它不知道A::f 的新声明的类型。如果A::f是一个非静态成员函数,那么它的类型是int () const,如果它是一个静态成员函数那么它的类型是int ()(没有隐含的const)。

Clang 3.1 并没有完全正确:它假设如果 constexpr 函数是成员函数,那么 constexpr 隐含地使其成为 const,这允许 #4 和 #5 工作,但会中断#6。 Clang 3.2 通过两次执行constexpr-implies-const 规则来解决这个问题:一次在重新声明匹配中(这样#5 被认为重新声明了#2 而不是#1,即使它还没有隐式地const ),并且一旦选择了先前的声明(将隐式 const 添加到#5)。

【讨论】:

    【解决方案2】:

    虽然标准没有明确提到是否允许 constexpr 静态成员函数的定义与其声明分开,但它在 7.1 下提供了 constexpr 构造函数的单独定义的以下示例。 5p1:

    struct pixel {
      int x;
      int y;
      constexpr pixel(int); // OK: declaration
    };
    constexpr pixel::pixel(int a)
      : x(square(a)), y(square(a)) // OK: definition
      { }
    

    所以很明显constexpr 函数可以有单独的声明和定义。同样在 7.1.5p1:

    如果有任何声明 函数或函数模板具有 constexpr 说明符,则其所有声明应包含 constexpr 说明符。

    这意味着constexpr 函数可以有(多个)非定义声明。

    【讨论】:

      【解决方案3】:

      我很确定 g++ 是正确的。事实上,这曾经是 g++ 中的 bug。我在标准中找不到明确说明您可以将静态 constexpr 声明与定义分开的地方,但如果您查看第 7.1.5 节讨论 constexpr 说明符 (summarized here),它不会t 排除,一般表示允许。

      【讨论】:

        猜你喜欢
        • 2018-02-08
        • 2020-06-12
        • 2019-05-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多