在 clang 中显式指定的参数无效,但在 gcc 中成功编译 — 谁错了?
Invalid explicitly-specified argument in clang but successful compilation in gcc — who's wrong?
以下代码在g++中编译没有问题:
#include <iostream>
#include <string>
#include <tuple>
template<typename T>
void test(const T& value)
{
std::tuple<int, double> x;
std::cout << std::get<value>(x);
}
int main() {
test(std::integral_constant<std::size_t,1>());
}
我使用了这个命令:
g++ test.cpp -o test -std=c++14 -pedantic -Wall -Wextra
但当我将g++
切换到clang++
时(使用g++5.1.0和clang++3.6.0),我会得到以下错误:
test.cpp:9:18: error: no matching function for call to 'get'
std::cout << std::get<value>(x);
^~~~~~~~~~~~~~~
test.cpp:13:5: note: in instantiation of function template specialization 'test<std::integral_constant<unsigned long, 1> >' requested here
test(std::integral_constant<std::size_t,1>());
^~~~~~~~~~~~~~~
<skipped>
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/tuple:867:5: note: candidate template ignored: invalid explicitly-specified argument for template parameter '_Tp'
get(tuple<_Types...>& __t) noexcept
^
以及用于std::get
的其他过载的类似note:
条目。
但我将std::integral_constant
传递给test()
,这是一个常量表达式,为什么它会是模板参数的"无效显式指定参数"?是叮当声还是我做错了什么?
我注意到,如果我将test()
的参数从const T&
更改为const T
,则clang编译成功。我是否通过引用传递integral_constant
的constexpr
质量而丢失了它?
由于一周内没有答案,我将发布我的愿景。我远不是一个语言写作专家,实际上我认为自己是一个完全的新手,但仍然如此。以下是基于我对标准的阅读,以及我最近的问题。
因此,首先让我们以以下方式重写代码:
struct A {
constexpr operator int() const { return 42; }
};
template <int>
void foo() {}
void test(const A& value) {
foo<value>();
}
int main() {
A a{};
test(a);
}
它表现出相同的行为(使用gcc构建,使用clang失败并出现类似错误),但是:
- 在
test()
没有模板类型推导,以确保问题与类型推导无关 - 使用"mocks"而不是CCD_ 13成员来确保这不是他们实现的问题
- 并且具有显式变量
a
,而不是临时变量,这将在后面解释
这里发生了什么?我将引用N4296。
我们有一个带有非类型参数的模板foo
。
[114.3.2(温度参数非类型)]/1:
非类型模板参数的模板参数应为类型的转换常量表达式(5.20)模板参数。
因此,模板参数,即value
,应该是类型为int
的转换常量表达式。
[5.20(expr.const)]/4:
T类型的转换常量表达式是一个隐式表达式转换为类型T,其中转换的表达式是常量表达式和隐式转换序列只包含
- 用户定义的转换
- 。。。(投下无关的子弹)
并且其中参考结合(如果有的话)直接结合。
我们的表达式(value
)可以隐式转换为类型int
,并且转换序列只包含用户定义的转换。因此,两个问题仍然存在:"转换后的表达式是否是常量表达式"answers"引用绑定(如果有)是否直接绑定"。
对于第一个问题,我认为短语"转换后的表达式"的意思是已经转换为int
的表达式,这有点像static_cast<int>(value)
,而不是原始表达式(value
)。为此,
[5.20(expr.const)]/2:
条件表达式
e
是核心常量表达式,除非CCD_ 24的评估遵循抽象机(1.9)的规则,将评估以下表达式之一:
- 。。。(省略了一长串)
对我们的表达式static_cast<int>(value)
的评估仅导致对A::operator int()
的评估,即constexpr
,因此是明确允许的。不评估A
的任何成员(如果有的话),也不评估其他任何成员。
因此,static_cast<int>(value)
是一个常数表达式。
关于第二个问题,关于引用绑定,我根本不清楚这指的是哪个过程。然而,无论如何,我们的代码中只有一个引用(const A& value
),它直接绑定到main
的变量a
(这就是我引入a
的原因)。
实际上,直接绑定是在[8.5.3(dcl.init.ref)]/5:末尾定义的
在除最后一种情况外的所有情况下(即,创建和初始化来自初始值设定项表达式的临时),则引用被称为直接绑定到初始值设定项表达式。
这个"最后一个"情况似乎指的是5.2,非直接绑定意味着从临时(如const int& i = 42;
)初始化,而不是当我们有非临时a
时的情况。
UPD:我问了一个单独的问题来检查我对上述标准的理解是否正确。
因此,底线是代码应该是有效的,而clang是错误的。我建议你向clang bug跟踪器提交一个bug,并参考这个问题。或者,无论出于何种原因,你都不会提交错误,请告诉我,我会提交的。
UPD:提交错误报告
- CMake项目Boost库错误:Boost/config/compiler/gcc.hpp:165:10:致命错误:cs
- cmake在我的项目中所需的所有静态库都不成功
- 尽管测试成功,CppUnit测试核心仍被丢弃.为什么
- 奇怪的结构&GCC&clang(void*返回类型)
- GCC本机矩阵运算库
- PowerPC ppc64le上的Gcc Woverloaded虚拟错误
- gcc和c++17的过载解析失败
- 数据成员SFINAE的C++17测试:gcc vs clang
- GCC对可能有效的代码抛出init list生存期警告
- 如何解决gcc编译器优化导致的centos双编译器设置中的分段错误
- 使用 GCC 卸载的 OpenMP 卸载失败,并出现"Ptx assembly aborted due to errors"
- 为什么与常规GCC不同,即使有"学究性错误",MinGW-GCC也能容忍丢失的返回类型
- 使用gcc从静态链接的文件中查找可选符号
- 普通环路未使用gcc 4.8.5自动矢量化
- 如何让LLDB在成功时退出,在失败时等待
- 未知的 GCC 链接器错误,但已成功构建
- 链接使用 arm-none-eabi-g++ 成功,但不能使用 arm-none-eabi-gcc
- 使用CUDA nvcc编译时,与提升库的链接失败,使用gcc编译成功
- 在 clang 中显式指定的参数无效,但在 gcc 中成功编译 — 谁错了?
- 安装gcc 4.7,但无法成功运行程序