内联constexpr函数定义是否合法? gcc(ok)vs clang(错误)

时间:2012-12-17 01:29:06

标签: 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也接受这个变种。根据我的理解,两个版本应该根据标准相同。这是一个铿锵的错误还是我错过了什么?

3 个答案:

答案 0 :(得分:10)

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

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 - 隐含 - const规则来解决这个问题:一次在重新声明匹配中(这样#5被认为是重新声明#2而不是#1,即使它还没有隐式const),并且一旦选择了先前的声明(将隐式const添加到#5),就会再次隐藏。

答案 1 :(得分:8)

虽然标准没有明确提及是否允许constexpr静态成员函数的定义与其声明分开,但它具有以下constexpr构造函数的单独定义示例,在7.1.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函数可以有(多个)非定义声明。

答案 2 :(得分:2)

我很确定g ++是正确的。事实上,这曾经是g ++中的bug。我无法在标准中找到一个明确表示你可以将静态constexpr声明与定义分开的地方,但是如果你看一下7.1.5讨论constexpr说明符(summarized here),那么不排除它,这通常意味着它是允许的。