我当前的程序被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=c11-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是错的?这个程序应该是合法的C11。
有趣的旁白。如果one
在结构中实现,clang不再抱怨:
struct A {
static constexpr inline int one() { return 1; }
}
gcc也接受这个变体。根据我的理解,根据标准,两个版本应该是相同的。是一只叮当的虫子还是我错过了什么?
这是一个Clang错误(在Clang 3.2中修复)。问题是Clang在确定函数的重新声明是否与之前的声明匹配时,没有正确处理隐式const
ness的影响。考虑:
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代码>)。
clang3.1并没有完全正确:它假设如果一个CONSTEXR
函数是一个成员函数,那么CONSTEXR
使它隐式地const
,它允许#4和#5工作,但破坏#6。clang 3.2通过两次实现conexpr
-implies-const
规则来修复这个问题:一次是在重新声明匹配中(这样#5被认为是重新声明#2而不是#1,即使它还没有隐式地const
),然后再次一旦选择了先前的声明(将隐式常量添加到#5)。
尽管该标准没有明确提到是否允许将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:
如果函数或函数模板的任何声明具有constepr
说明符,则其所有声明都应包含constepr
说明符。
这意味着constexpr
函数可以有(多个)非定义声明。
我很确定g是正确的。事实上,这曾经是g中的一个bug。我在标准中找不到明确指出可以将静态constexpr声明与定义分开的地方,但是如果您查看第7.1节。5关于constexpr说明符(这里总结),它不排除它,这通常意味着它是允许的。