具有未使用的引用参数的 constexpr 函数——gcc vs clang
constexpr function with unused reference argument – gcc vs clang
考虑以下代码:
template <int N, typename T> void f(T) { }
template <typename T>
constexpr int k(T&) { return 0; }
int main()
{
constexpr auto i = 1;
f<k(i)>([&i]
{
f<k(i)>(0);
});
}
clang++
(trunk) 编译它。 g++
(trunk) 失败并出现以下错误:
<source>: In lambda function:
<source>:11:19: error: no matching function for call to 'f<k<const int>((* & i))>(int)'
11 | f<k(i)>(0);
| ^
<source>:1:35: note: candidate: 'template<int N, class T> void f(T)'
1 | template <int N, typename T> void f(T) { }
| ^
<source>:1:35: note: template argument deduction/substitution failed:
<source>:11:19: error: '__closure' is not a constant expression
11 | f<k(i)>(0);
| ^
<source>:11:13: note: in template argument for type 'int'
11 | f<k(i)>(0);
| ~^~~
将 k(T&)
更改为 k(T)
即可解决问题。在我看来,问题与参考参数不是 常量表达式 但它未用作 k
.
的一部分有关
这里哪个编译器是正确的?
出现在 lambda 表达式复合语句内但不在其外的表达式 k(i)
会发出错误。这是一个 GCC 错误。根据[expr.prim.lambda.capture]/11
An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference.
所以 lambda 之外的 k(i)
与 lambda 之外的 k(i)
是相同的表达式,因此 GCC 没有理由为第二个表达式而不是第一个表达式发出错误。
在 godbolt 上编译时出现 0 个错误。
我使用变量 result_k = k(i);
来绕过这个错误。
template <int N, typename T> void f(T) { }
template <typename T> constexpr int k(T&) { return 0; }
int main() {
constexpr auto i = 1;
const int result_k=k(i);
f<result_k>([&i]{ f<result_k>(0);});
}
这里 GCC 是正确的。
An expression e
is a core constant expression unless the evaluation
of e
, following the rules of the abstract machine, would evaluate
one of the following expressions:
- ...
- in a lambda-expression, a reference to [...] a variable with automatic storage duration defined outside that lambda-expression,
where the reference would be an odr-use; ...
- ...
k(i)
odr-uses i
因此 k(i)
不是 lambda 表达式中的常量表达式,因此此代码格式错误。
考虑以下代码:
template <int N, typename T> void f(T) { }
template <typename T>
constexpr int k(T&) { return 0; }
int main()
{
constexpr auto i = 1;
f<k(i)>([&i]
{
f<k(i)>(0);
});
}
clang++
(trunk) 编译它。 g++
(trunk) 失败并出现以下错误:
<source>: In lambda function: <source>:11:19: error: no matching function for call to 'f<k<const int>((* & i))>(int)' 11 | f<k(i)>(0); | ^ <source>:1:35: note: candidate: 'template<int N, class T> void f(T)' 1 | template <int N, typename T> void f(T) { } | ^ <source>:1:35: note: template argument deduction/substitution failed: <source>:11:19: error: '__closure' is not a constant expression 11 | f<k(i)>(0); | ^ <source>:11:13: note: in template argument for type 'int' 11 | f<k(i)>(0); | ~^~~
将 k(T&)
更改为 k(T)
即可解决问题。在我看来,问题与参考参数不是 常量表达式 但它未用作 k
.
这里哪个编译器是正确的?
出现在 lambda 表达式复合语句内但不在其外的表达式 k(i)
会发出错误。这是一个 GCC 错误。根据[expr.prim.lambda.capture]/11
An id-expression within the compound-statement of a lambda-expression that is an odr-use of a reference captured by reference refers to the entity to which the captured reference is bound and not to the captured reference.
所以 lambda 之外的 k(i)
与 lambda 之外的 k(i)
是相同的表达式,因此 GCC 没有理由为第二个表达式而不是第一个表达式发出错误。
在 godbolt 上编译时出现 0 个错误。
我使用变量 result_k = k(i);
来绕过这个错误。
template <int N, typename T> void f(T) { }
template <typename T> constexpr int k(T&) { return 0; }
int main() {
constexpr auto i = 1;
const int result_k=k(i);
f<result_k>([&i]{ f<result_k>(0);});
}
这里 GCC 是正确的。
An expression
e
is a core constant expression unless the evaluation ofe
, following the rules of the abstract machine, would evaluate one of the following expressions:
- ...
- in a lambda-expression, a reference to [...] a variable with automatic storage duration defined outside that lambda-expression, where the reference would be an odr-use; ...
- ...
k(i)
odr-uses i
因此 k(i)
不是 lambda 表达式中的常量表达式,因此此代码格式错误。