c++ - 内联 constexpr 函数定义是否合法? gcc (ok) vs clang (error)

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

我当前的程序被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错误还是我错过了什么?

最佳答案

这是一个 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)。

关于c++ - 内联 constexpr 函数定义是否合法? gcc (ok) vs clang (error),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13907252/

相关文章:

c++ - 模板类的唯一编号

clang - 为什么 clang/llvm 在构建碳时使用 Bazel 向我抛出这个错误

c - MacOS 从 Mavericks Lion 兼容的可执行文件编译

c++ - 为什么 gcc-4.9.2 不支持 std::string.insert(iterator, range) 返回迭代器

c++ - 使用 std::function 参数重载函数:为什么从未调用 const 方法?

c++ - clang 实现 char8_t 的方式是否存在缺陷,或者标准的某些暗角是否禁止优化?

c++ - 尽管有 dllimport,但在两个 DLL 中导出的数据符号

c++ - 如何为 std::priority_queue 的比较器编写带有捕获的 lambda

c++ - 有没有办法初始化大括号中的成员?

c++ - 调用 std::map::emplace() 并避免不必要的构造