struct A {
consteval A() {};
};
constexpr bool g() {
auto a = new A;
delete a;
return true;
}
int main() {
static_assert(g());
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/jsq35WxKs
GCC 和 MSVC 拒绝该程序,ICC 和 Clang 接受它:
///MSVC:
<source>(6): error C7595: 'A::A': call to immediate function is not a constant expression
Compiler returned: 2
//GCC:
<source>: In function 'constexpr bool g()':
<source>:6:18: error: the value of '<anonymous>' is not usable in a constant expression
6 | auto a = new A;
| ^ …
Run Code Online (Sandbox Code Playgroud) 在此代码中,
template<class T, class U>
concept always_true = true;
template<class T>
concept always_true_if_tagged = always_true<T, typename T::tag>;
struct A {
using tag = int;
};
static_assert(always_true_if_tagged<A>);
static_assert(!always_true_if_tagged<int>); //GCC says this failed
Run Code Online (Sandbox Code Playgroud)
GCC 表示第二个断言失败。Clang 和 MSVC 都同意编译它。
我最初认为它是不正确的,不需要诊断,因为temp.constr.normal#1.4
概念 ID 的范式
C<A1, A2, ..., An>
是 的约束表达式的范式C
,在替换A1, A2, ..., An
每个C
原子约束中的参数映射中的各自模板参数后。如果任何此类替换导致无效类型或表达式,则该程序格式错误;无需诊断。
替换T::typename tag
是 的参数映射always_true
,因此格式错误;无需诊断。
所以我的前两个问题是
解决方案之一是检查之前的嵌套类型名。所以参数映射always_true
不会发生。
template<class T>
concept always_true_if_tagged =
requires …
Run Code Online (Sandbox Code Playgroud) 最新的标准草案N4910在 [temp.over.link] 中有关于功能等效性的示例:
template<int I> concept C = true;
template<typename T> struct A {
void f() requires C<42>; // #1
void f() requires true; // OK, different functions
};
Run Code Online (Sandbox Code Playgroud)
我的理解是,这没问题,因为C<42>
和true
是未评估的操作数。因此,根据[temp.over.link]/5,在考虑约束是否在功能上等价时,不是表达式的结果,而是对哪个实体执行什么操作以及以什么顺序执行,才是决定约束功能等价的。
但是,如果约束在功能上等效,那么由于它们不等效,根据[temp.over.link]/7,程序将格式错误,无需诊断,因为两次声明同一成员将使程序格式错误。
另一方面
template<typename>
requires C<42>
void g() {};
template<typename>
requires true
void g() {};
Run Code Online (Sandbox Code Playgroud)
似乎格式不正确,不需要诊断,因为[temp.over.link]/6表示如果模板头接受并满足相同的模板参数,则它们在功能上是等效的。
我是否误解了示例并引用了标准措辞,或者真的有这样的差异吗?如果是这样,为什么?
考虑:
#include <type_traits>
template <typename T>
struct A {
~A() requires(std::is_void_v<T>);
//~A() requires(!std::is_void_v<T>) = default;
};
template struct A<int>;
Run Code Online (Sandbox Code Playgroud)
我认为一个类不可能没有析构函数,gcc 13.2 似乎同意我的观点,但 clang 17.0 和 msvc 19.38 不同意并抱怨:
[clang] error: no viable destructor found for class 'A<int>'
[msvc ] error C7653: 'A<int>': failed to select a destructor for the class
Run Code Online (Sandbox Code Playgroud)
(将注释行添加到代码中可以解决此问题并使所有编译器满意。)
哪些编译器是正确的?标准中的相关引用是什么?
请注意,在提出此问题后,缺陷报告更改了下面提到的行为。见问题末尾。
考虑以下:
// Variant 1
template<auto> struct require_constexpr;
template<typename R>
constexpr auto is_constexpr_size(R&& r) {
return requires { typename require_constexpr<std::ranges::size(std::forward<R>(r))>; };
}
static_assert(!is_constexpr_size(std::vector{1,2,3,4}));
static_assert(is_constexpr_size(std::array{1,2,3,4}));
Run Code Online (Sandbox Code Playgroud)
这里的目标不是is_constexpr_size
函数本身,而是找到一个 ( requires
) 表达式,确定范围类型的大小是编译时常量,以便可以在按顺序通过转发引用获取任何范围的函数中使用它if constexpr
基于它进行切换。
不幸的是,这不起作用,因为r
它是引用类型并且不能在常量表达式中使用,尽管std::array
调用std::range::sizes
永远不会访问引用的对象。
变体 2:在函数参数中替换为 会改变这一点R&&
。R
非引用类型变量的常量表达式要求较弱,MSVC 和 GCC 都接受经过此更改的代码,但 Clang 仍然不接受。我的理解是,目前有一项更改规则的提案,以便 with 的变体R&&
也能按预期工作。
然而,在实现这一点之前,我正在寻找一种替代方案,不需要将参数限制为非引用类型。我也不想依赖于范围的类型,例如默认可构造的类型。因此我无法构造正确类型的临时对象。std::declval
也无法使用,因为std::ranges::size
需要评估。
我尝试了以下方法:
// Variant 3
return requires (std::remove_reference_t<R> s) { typename require_constexpr<std::ranges::size(std::forward<R>(s))>; };
Run Code Online (Sandbox Code Playgroud)
这被 MSVC 接受,但不被 …
#include<type_traits>
template <typename T, T>
struct A { };
template <typename T, T t>
void f(A<T, t>) {
}
int main() {
f(A<const int, 0>{});
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/n6bcj5rjM
该程序在 C++14 和 C++17 模式下被 GCC 和 ICC 接受,在 C++14 模式下被 Clang 接受,但在 C++17 模式下被 Clang 拒绝,在任一模式下被 MSVC 拒绝。
拒绝是通过这样的诊断:
<source>:12:5: error: no matching function for call to 'f'
f(A<const int, 0>{});
^
<source>:7:6: note: candidate template ignored: deduced conflicting types for parameter 'T' ('const int' vs. 'int')
void f(A<T, t>) {
^ …
Run Code Online (Sandbox Code Playgroud) c++ language-lawyer template-argument-deduction non-type-template-parameter
#include<cstring>
struct A {
char a;
int b;
};
int main() {
A* a = new A();
a->a = 1;
unsigned char m[sizeof(A)];
std::memcpy(m, a, sizeof(A));
return m[1];
}
Run Code Online (Sandbox Code Playgroud)
除了由于分配失败而可能出现的异常以及假设在和之间0
至少有一个填充字节之外,该程序是否保证以 C++ 中的状态退出?a
b
A
new A()
进行值初始化,因为A
的默认构造函数很简单,但既不是用户提供的也不是删除的,因此将对象的所有成员和填充字节归零A
。
对于 C,N1570(C11 草案)中的 6.2.6.1p6 对我来说似乎暗示填充字节在分配给成员后处于未指定的状态,尽管我可能会误解这一点(请参阅评论)。但无论如何,我在 C++ 标准(草案)中没有看到任何允许这样做的规则。
受此启发,如果在第二个(不兼容)示例中分配给成员,则零初始化结构的填充可能会泄漏信息。但请注意,该示例的描述无论如何都是错误的,因为它实际上执行聚合初始化,而不是值初始化,因此没有零初始化。
这是我之前在问题中使用的代码的两个类似版本,但由于与我用来检查对象表示的方法不相关的问题,它们可能具有 UB(请参阅注释):
#include<new>
struct A {
char a;
int b;
};
int main() {
unsigned char* m = new unsigned char[sizeof(A)];
A* a = new(m) A(); …
Run Code Online (Sandbox Code Playgroud) 即使任何构造函数都没有使用默认成员初始值设定项,在默认成员初始值设定项中使用仍然是 odr 使用吗?
例如,该程序是否格式错误,因为g<A>
使用了 odr,因此其定义被隐式实例化?
template<typename T>
void g() { sizeof(T); }
struct A;
struct B {
B() : i{} {};
int i = (&g<A>, 0);
};
int main() { }
Run Code Online (Sandbox Code Playgroud)
MSVC 认为不会。Clang、GCC 和 ICC 认为是的。https://godbolt.org/z/zrr9oEdfe
c++ one-definition-rule template-instantiation implicit-instantiation
struct A {
~A() {}
consteval A() {}
consteval auto f() {}
};
int main() {
A{};
//A{}.f(); //1
}
Run Code Online (Sandbox Code Playgroud)
https://godbolt.org/z/4KPY5P7o7
该程序被 ICC、GCC 和 Clang 接受,但被 MSVC 拒绝,抱怨析构函数不在constexpr
立即函数调用中。
添加标记行//1
会导致所有四个编译器拒绝该代码。
问题:无论哪种情况,编译器都正确吗?如果正确,为什么?
请注意,这里有趣的部分是,A
由于非constexpr
平凡的析构函数,它是非字面的。删除其声明后,所有编译器都接受带有和不带有 的变体//1
。
constexpr
对于/函数和常量表达式,有一些特定于非文字类型的限制consteval
,但我认为它们中的任何一个都不应该适用于此。这些限制包括返回类型、参数类型、局部变量定义的类型、右值到左值的转换以及对象的修改。我想这里只有最后一个可以申请。但是[expr.const]/5.16中的修改到底意味着什么以及此处将修改哪个对象?
我还认为 MSVC 的抱怨是不正确的,因为对象的销毁不应该成为其构造函数立即调用的一部分。
另请参阅我之前启发这一问题的问题:constexpr 函数中的 Consteval 构造函数和成员函数调用
[class.temporary]/7中当前的 C++ 标准草案包含短语
除函数参数对象之外的临时对象
我的印象是函数参数对象不是临时对象。然而,这个短语是最近才添加的。那么我是否错误或误解了上下文?
c++ ×10
c++20 ×5
c++-concepts ×3
consteval ×2
c++23 ×1
constexpr ×1
non-type-template-parameter ×1
padding ×1
std-ranges ×1