小编use*_*522的帖子

constexpr 上下文中带有 consteval 构造函数的新表达式

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)

c++ language-lawyer constexpr c++20 consteval

35
推荐指数
1
解决办法
1244
查看次数

为什么参数映射中的替换失败被认为是格式错误的?

此代码中,

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,因此格式错误;无需诊断。

所以我的前两个问题是

  1. 我说得对吗?(格式是否错误?我是否引用了正确的原因?)
  2. 为什么它应该是格式错误的?(如果我是对的。)

解决方案之一是检查之前的嵌套类型名。所以参数映射always_true不会发生。

template<class T>
concept always_true_if_tagged =
    requires …
Run Code Online (Sandbox Code Playgroud)

c++ language-design language-lawyer c++-concepts c++20

25
推荐指数
1
解决办法
909
查看次数

模板约束与成员函数约束的功能等效性

最新的标准草案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表示如果模板头接受并满足相同的模板参数,则它们在功能上是等效的。

我是否误解了示例并引用了标准措辞,或者真的有这样的差异吗?如果是这样,为什么?

c++ language-lawyer c++-concepts c++23

18
推荐指数
1
解决办法
500
查看次数

类可以坚不可摧(没有析构函数)吗?

考虑:

#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)

(将注释行添加到代码中可以解决此问题并使所有编译器满意。)

哪些编译器是正确的?标准中的相关引用是什么?

c++ language-lawyer c++20

18
推荐指数
1
解决办法
674
查看次数

检测范围大小的编译时常数

请注意,在提出此问题后,缺陷报告更改了下面提到的行为。见问题末尾。


编译器资源管理器链接

考虑以下:

// 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 接受,但不被 …

c++ language-lawyer c++-concepts c++20 std-ranges

15
推荐指数
1
解决办法
903
查看次数

非类型模板参数推导中的类型冲突

#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

13
推荐指数
1
解决办法
498
查看次数

分配后填充字节

#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++ 中的状态退出?abA

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)

c++ padding language-lawyer

12
推荐指数
1
解决办法
873
查看次数

在未使用的默认成员初始值设定项中使用仍然是 odr 使用吗?

即使任何构造函数都没有使用默认成员初始值设定项,在默认成员初始值设定项中使用仍然是 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

12
推荐指数
1
解决办法
237
查看次数

非文字类型和常量表达式

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 构造函数和成员函数调用

c++ language-lawyer c++20 consteval

10
推荐指数
1
解决办法
908
查看次数

函数参数对象是临时对象吗?

[class.temporary]/7中当前的 C++ 标准草案包含短语

除函数参数对象之外的临时对象

我的印象是函数参数对象不是临时对象。然而,这个短语是最近才添加的。那么我是否错误或误解了上下文?

c++ language-lawyer temporary-objects

10
推荐指数
1
解决办法
348
查看次数