gpt4 book ai didi

c++ - 常量表达式中的模板化委托(delegate)复制构造函数

转载 作者:行者123 更新时间:2023-12-01 13:06:46 25 4
gpt4 key购买 nike

这个问题的动机是 this one .

考虑以下代码:

struct B {};

struct S {
B b; // #1

S() = default;

template <typename ...dummy> // #2
constexpr S(const S&) {}

template <typename ...dummy> // #3
constexpr S(S &other)
: S(const_cast<const S&>(other)) // #4
{}
};

S s;
constexpr S f() {return s;}

int main() {
constexpr auto x = f();
}

GCC 成功编译了这段代码,但 Clang 拒绝了它( Example on Godbolt.org )。 Clang 产生的错误信息是
<source>:21:20: error: constexpr variable 'x' must be initialized by a constant expression
constexpr auto x = f();
^ ~~~
<source>:13:11: note: read of non-constexpr variable 's' is not allowed in a constant expression
: S(const_cast<const S&>(other))
^
<source>:13:11: note: in call to 'S(s)'
<source>:18:25: note: in call to 'S(s)'
constexpr S f() {return s;}
^
<source>:21:24: note: in call to 'f()'
constexpr auto x = f();
^
<source>:17:3: note: declared here
S s;
^

请注意,如果我们删除 #2、#3 或 #4 中的任何一个,两个编译器都会接受此代码。如果我们将 #1 替换为 int b = 0; , both compilers reject it .

我的问题是:
  • 根据当前标准,哪个编译器是正确的?
  • 如果 GCC 是正确的,为什么用 int b = 0; 替换 #1使此代码格式错误?如果 Clang 是正确的,为什么删除 #2、#3 或 #4 中的任何一个会使这段代码格式正确?
  • 最佳答案

    由于您的两个用户定义的构造函数都是模板,they are not复制(或移动)构造函数。因此编译器隐式声明了一个复制构造函数,并将其定义为默认值。

    因此,第 1 部分归结为以下区别程序:

    struct A {
    struct B {} b;
    constexpr A() {};
    // constexpr A(A const& a) : b{a.b} {} // #1
    };
    int main() {
    auto a = A{};
    constexpr int i = (A{a}, 0);
    }

    Rejected通过 Clang 和 MSVC,被 gcc 接受;取消评论 #1让三个人都接受。

    根据 the implicitly-defined copy constructor 的定义没有办法 #1constexpr A(A const&) = default; 有什么不同所以 gcc 是正确的。还要注意,如果我们给出 B用户定义的 constexpr 复制构造函数 Clang 和 MSVC 再次接受,因此问题似乎是这些编译器无法跟踪递归空隐式可复制类的 constexpr 复制构造性。为 MSVC 提交错误和 Clang ( fixed 用于 Clang 11)。

    第2部分:

    删除 #1表示您正在复制(执行左值到右值转换)一个对象 s.b类型 int ,其生命周期开始于 constexpr 上下文之外。

    删除 #2S一个用户定义的 constexpr复制构造函数,然后委托(delegate)给 #4 .

    删除 #3S用户定义的(非常量)复制构造函数,抑制隐式定义的复制构造函数,因此委托(delegate)构造调用模板 const 构造函数(记住,它不是复制构造函数)。

    删除 #4表示您的构造函数模板带有参数 S& other不再调用隐式定义的复制构造函数,所以 b是默认初始化的,Clang 可以在 constexpr 上下文中执行此操作。请注意,复制构造函数仍被隐式声明并定义为默认值,只是您的构造函数 template<class...> S::S(S& other)是重载决议的首选。

    重要的是要认识到抑制隐式定义的复制构造函数和提供首选重载之间的区别。 template<class...> S::S(S&)不抑制隐式定义的复制构造函数,但它是非 const 左值参数的首选,假设隐式定义的复制构造函数具有参数 S const& .另一方面, template<class...> S::S(S const&)不会抑制隐式定义的复制构造函数,并且永远不能优先于隐式定义的复制构造函数,因为它是一个模板并且参数列表是相同的。

    关于c++ - 常量表达式中的模板化委托(delegate)复制构造函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60472257/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com