编译器之间在丢弃的 if constexpr(false) 语句中实例化模板的行为不一致
Inconsistent behaviour across compilers in regard to instantiation of a template in a discarded if constexpr(false) statement
我试图了解下面的代码片段是否应该根据标准进行编译。当我尝试使用三个主要编译器的最新版本编译它时,会发生以下情况:
-
✓ Clang (版本 7.0.0,带
-std=c++17
标志):编译正常; -
✓ GCC(8.2版,带有
-std=c++17
标志):也可以编译正常; -
❌ MSVC(版本 19.16,带
/std:c++17
标志):编译器错误(见下文)。
发生此错误的原因是 MSVC 编译器似乎尝试实例化std::optional<void>
尽管代码已被丢弃。GCC和Clang似乎没有这样做。
《标准》是否明确规定了在这种情况下应该发生什么?
#include <optional>
#include <type_traits>
template<typename T, typename... Args>
struct Bar
{
void foo(Args... args)
{
if constexpr(!std::is_same_v<T, void>) // false
{
// MSVC compiler error occurs because of the line below; no error occurs when compiling with GCC and Clang
std::optional<T> val;
}
}
};
int main(int argc, char** argv)
{
Bar<void, int> inst;
inst.foo(1);
return 0;
}
MSVC 的错误:
C:/msvc/v19_16/includeoptional(87): error C2182: '_Value': illegal use of type 'void' C:/msvc/v19_16/includeoptional(128): note: see reference to class template instantiation 'std::_Optional_destruct_base<_Ty,false>' being compiled with [ _Ty=void ]
现场演示
绝对是MSVC的一个错误。存在错误报告,据报道已在Visual Studio 2019预览版中修复。
if constexpr
在[stmt.if]/2
中标准化:
如果
if
语句的形式是if constexpr
,则条件的值应是上下文转换的布尔类型的常量表达式;这种形式称为constexpr if语句。
这适用。
如果转换条件的值为 false,则第一个子语句是丢弃的语句,否则 [...]。
它也适用,在您的程序中{ std::optional<T> val; }
丢弃语句。
在封闭模板化实体(ndYSC
Bar<void, int>
)的实例化过程中,如果条件在实例化后不依赖于值,则丢弃的子语句(如果有)不会被实例化。
除了@YSC的回答外,还有相关的[temp.inst]/10
:
实现不应隐式实例化函数模板、变量模板、成员模板、非虚拟成员函数、成员类、类模板的静态数据成员或 constexpr if 语句的子语句,除非需要此类实例化。
我可以观察到该问题仅部分修复(VS 16.6.0 预览版 3.0 - cl 版本 19.26.28803.1)。现在您可以观察到以下内容:GodBolt
- 它适用于没有模板参数包的类。
- 它现在可以正确处理类内定义,但不能与类外定义一起使用。
(错误仅在默认启用的/permissive-
模式下发生)
#include <iostream>
#include <type_traits>
#include <optional>
#define IN_CLASS_DEF_FUNC 0
#define WITH_PARAM_PACK 1
//1, 1 -> works
//0, 1 -> error (invalid use of type void)
//0, 0 -> works
//1, 0 -> works
template<typename T
#if WITH_PARAM_PACK
, typename... Args
#endif
>
struct Bar
{
#if IN_CLASS_DEF_FUNC
void foo()
{
if constexpr (!std::is_same_v<T, void>) // false
{
// MSVC compiler error occurs because of the line below; no error occurs when compiling with GCC and Clang
std::optional<T> val;
}
}
#else
void foo();
#endif
};
#if !IN_CLASS_DEF_FUNC
template<typename T
#if WITH_PARAM_PACK
, typename... Args
#endif
>
void Bar<T
#if WITH_PARAM_PACK
, Args...
#endif
>::foo()
{
if constexpr (!std::is_same_v<T, void>) // false
{
// MSVC compiler error occurs because of the line below; no error occurs when compiling with GCC and Clang
std::optional<T> val;
}
}
#endif
int main(int argc, char** argv)
{
Bar<void> inst;
inst.foo();
Bar<int> inst_okay;
inst_okay.foo();
return 0;
}
顺便说一句:作为快速修复,您可以在没有参数包的情况下在独立函数中移动通用代码......
- 从C++实例化QML
- 设计一个只能由特定类实例化的类(如果可能的话,通过make_unique)
- 如何创建一个空的全局类并在启动时实例化它
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 约束和显式模板实例化
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 对象实例化调用构造函数的次数太多
- 如何使用非默认构造函数实例化模板化类
- 静态数据成员模板专用化的实例化点在哪里
- 错误的cv::face FacemarkLBF实例化
- C++的解析器在可以区分比较和模板实例化之前会做什么?
- 为什么 gcc 和 clang 为函数模板的实例化生成不同的符号名称?
- 检查某些类型是否是模板类 std::optional 的实例化
- 我有一个对象,它将在整个程序的持续时间内实例化,但一个类成员不会,我应该动态分配它吗?
- 无法使用 SWIG 在 Python 中实例化C++类(获取属性错误)
- 模板化类构造函数的模板实例化
- 在 c++ 中的模板实例化中使用带有构造函数的类作为类型参数
- 受约束的成员函数和显式模板实例化
- 对显式实例化的模板函数的未定义引用
- 编译器之间在丢弃的 if constexpr(false) 语句中实例化模板的行为不一致