仅限Clang:一对可以放置到一个向量中;一对;但不是对:为什么?

时间:2022-02-14 09:44:36

I have the following three snippets of code to demonstrate an easily reproducible issue.

我有以下三段代码来演示一个易于重现的问题。

using namespace boost::filesystem;
using namespace std;

int main()
{

    path dummy_path;

    // Snippet 1
    // Two paths
    // Succeeds
    //
    // vector<pair<path, path>> myvec;
    // myvec.emplace_back(dummy_path, dummy_path);

    // Snippet 2
    // Two unique_ptr's
    // Succeeds
    //
    // vector<pair<unique_ptr<int>, unique_ptr<int>>> myvec;
    // myvec.emplace_back(unique_ptr<int>(new int(13)), unique_ptr<int>(new int(12)));

    // Snippet 3
    // A path and a unique_ptr.
    //
    // **FAILS** on Clang, succeeds in Visual Studio
    //
    vector<pair<path, unique_ptr<int>>> myvec;
    myvec.emplace_back(dummy_path, unique_ptr<int>(new int(12)));

}

Here is the compiler error on Clang:

这是Clang上的编译器错误:

error: call to implicitly-deleted copy constructor of 'std::__1::unique_ptr<int, std::__1::default_delete<int> >' (in reference to the second member of the pair, the unique_ptr, obviously).

错误:调用隐式删除的'std :: __ 1 :: unique_ptr >>的复制构造函数(引用该对的第二个成员,显然是unique_ptr)。 ,std>

It seems that for some reason, the failing case indicated is causing the copy constructor of the pair, rather than the move constructor, to be called.

似乎由于某种原因,指示的失败案例导致调用该对的复制构造函数而不是移动构造函数。

This is Clang 5.0.2 on OS X 10.8.5. (And VS 11.0.60610.01 Update 3 on Windows 7 64-bit.)

这是OS X 10.8.5上的Clang 5.0.2。 (和Windows 11 64位上的VS 11.0.60610.01 Update 3。)

In my actual application, the data types are more complex but the error boils down to the one described in this question.

在我的实际应用中,数据类型更复杂,但错误归结为此问题中描述的错误。

My question is twofold: Why does the case indicated fail on Clang even though the other two cases, which cover both data types, succeed?

我的问题有两个:为什么案例表明Clang失败了,即使另外两个涵盖两种数据类型的案例都成功了?

Perhaps more importantly, however: What can I do to work around this problem? Because my actual application is more complex, I do not have the option of not performing the emplace (or something equivalent) of the given pair into the vector - but if there's any other way I can get past this Clang issue to get that pair into that vector, I would be very happy.

但也许更重要的是:我可以做些什么来解决这个问题?因为我的实际应用程序更复杂,我没有选择不执行给定对中的emplace(或类似的东西)到向量中 - 但是如果有任何其他方式我可以通过这个Clang问题来获得该对那个载体,我会很开心的。

2 个解决方案

#1


2  

This is a bug in libc++, sorry. It has been fixed on tip-of-trunk. I believe you can work around it by adding the following to your compile command:

这是libc ++中的一个错误,抱歉。它已经固定在行李箱上。我相信你可以通过在你的编译命令中添加以下内容来解决它:

-D_LIBCPP_TRIVIAL_PAIR_COPY_CTOR

#2


2  

The problem comes from the libc++ Standard Library, since it has much less pair constructors within.

问题来自libc ++标准库,因为它内部的对构造函数要少得多。

I.e. libstd++ has following constructor:

即libstd ++有以下构造函数:

template<class _U2, class = typename
       enable_if<is_convertible<_U2, _T2>::value>::type>
constexpr pair(const _T1& __x, _U2&& __y)
: first(__x), second(std::forward<_U2>(__y)) { }

Which allows to compile your sample on Linux (with clang++). But libc++ has only:

这允许在Linux上编译您的示例(使用clang ++)。但是libc ++只有:

pair(const pair&) = default;
pair(pair&&) = default;
constexpr pair();
pair(const T1& x, const T2& y);                          // constexpr in C++14
template <class U, class V> pair(U&& x, V&& y);          // constexpr in C++14
template <class U, class V> pair(const pair<U, V>& p);   // constexpr in C++14
template <class U, class V> pair(pair<U, V>&& p);        // constexpr in C++14
template <class... Args1, class... Args2>
    pair(piecewise_construct_t, tuple<Args1...> first_args,
         tuple<Args2...> second_args);

I guess, that the pair(const T1& x, const T2& y); is applied due to the first argument being non-rvalue reference.

我想,那对(const T1&x,const T2&y);由于第一个参数是非右值引用而被应用。


The "hardcore" solution is to place this constructor by hand - inside the system library. The pair is defined in utility file. If you want to be able to compile your code elsewhere, you may bundle the modified libc++ into your project - it's not a big deal, really.

“硬核”解决方案是手动放置此构造函数 - 在系统库中。该对在实用程序文件中定义。如果您希望能够在其他地方编译代码,可以将修改后的libc ++捆绑到您的项目中 - 这真的不是什么大问题。

#1


2  

This is a bug in libc++, sorry. It has been fixed on tip-of-trunk. I believe you can work around it by adding the following to your compile command:

这是libc ++中的一个错误,抱歉。它已经固定在行李箱上。我相信你可以通过在你的编译命令中添加以下内容来解决它:

-D_LIBCPP_TRIVIAL_PAIR_COPY_CTOR

#2


2  

The problem comes from the libc++ Standard Library, since it has much less pair constructors within.

问题来自libc ++标准库,因为它内部的对构造函数要少得多。

I.e. libstd++ has following constructor:

即libstd ++有以下构造函数:

template<class _U2, class = typename
       enable_if<is_convertible<_U2, _T2>::value>::type>
constexpr pair(const _T1& __x, _U2&& __y)
: first(__x), second(std::forward<_U2>(__y)) { }

Which allows to compile your sample on Linux (with clang++). But libc++ has only:

这允许在Linux上编译您的示例(使用clang ++)。但是libc ++只有:

pair(const pair&) = default;
pair(pair&&) = default;
constexpr pair();
pair(const T1& x, const T2& y);                          // constexpr in C++14
template <class U, class V> pair(U&& x, V&& y);          // constexpr in C++14
template <class U, class V> pair(const pair<U, V>& p);   // constexpr in C++14
template <class U, class V> pair(pair<U, V>&& p);        // constexpr in C++14
template <class... Args1, class... Args2>
    pair(piecewise_construct_t, tuple<Args1...> first_args,
         tuple<Args2...> second_args);

I guess, that the pair(const T1& x, const T2& y); is applied due to the first argument being non-rvalue reference.

我想,那对(const T1&x,const T2&y);由于第一个参数是非右值引用而被应用。


The "hardcore" solution is to place this constructor by hand - inside the system library. The pair is defined in utility file. If you want to be able to compile your code elsewhere, you may bundle the modified libc++ into your project - it's not a big deal, really.

“硬核”解决方案是手动放置此构造函数 - 在系统库中。该对在实用程序文件中定义。如果您希望能够在其他地方编译代码,可以将修改后的libc ++捆绑到您的项目中 - 这真的不是什么大问题。