使用 gcc 和 clang 构建 C++ std::vector 时的不同行为

Sla*_*vov 8 c++ gcc clang

在下面的示例中,我期望v有 1 个元素,其var类型为Vector并将包含两个“int” Variant10 和 20。这是我可以通过 gcc 看到的行为。

对于 clang,“v”包含两个元素,即两个“int” Variant10 和 20。

我认为gcc的向量是通过initializer_list构造函数创建的,而clang的向量是通过move构造函数创建的

这是其中一个编译器的错误吗?我是否需要创建Variant的构造函数explicit(这将迫使我像 一样使用它Variant::Vector v{Variant{Variant::Vector{10, 20}}};。如果我想让构造函数保持非显式,是否有其他方法可以避免此问题?

在https://wandbox.org/中的所有 gcc 和 clang 版本中尝试了此代码,其行为相同。这里有一些直接尝试的链接:gcc、clang

#include <iostream>
#include <variant>
#include <vector>

struct Variant
{
    using Vector = std::vector<Variant>;

    Variant(const Vector & value)
    {
        var = value;
    }

    Variant(Vector && value)
    {
        var = std::move(value);
    }

    Variant(int value)
    {
        var = value;
    }

    std::variant<Vector, int> var;
};

int main()
{
    Variant::Vector v{Variant::Vector{10, 20}};

    std::cout << "v size: " << v.size() << ", index: " << v.at(0).var.index() << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

eca*_*mur 4

这是CWG 2137,目前只有 gcc 实现。

铿锵错误:https://github.com/llvm/llvm-project/issues/24186

另请参见C++ 构造函数采用大小为 1 的 std::initializer_list - 这稍微复杂一些,因为初始化程序列表元素可以从其参数(正在初始化的类型)构造,但诊断是相同的,我不能比 TC 的描述更好:

Clang 实现了 DR 1467(从 T 中用大括号初始化 T 的行为就好像您没有使用大括号一样),但尚未实现 DR 2137(转念一想,仅对聚合执行此操作)。

如果您可以稍微更改程序语法,则可以添加另一级大括号:

Variant::Vector v{{Variant::Vector{10, 20}}};
Run Code Online (Sandbox Code Playgroud)

或者添加括号:

Variant::Vector v({Variant::Vector{10, 20}});
Run Code Online (Sandbox Code Playgroud)