1_54版本中的boost variant出现了问题?

6
我认为 Boost::variant 在 1_54 版本中出现了问题。我正在尝试使用 std::unique_ptr 作为 boost variant 中的有限类型。
根据 1_54 文档,variant 需要具备可复制构造或可移动构造的能力。

http://www.boost.org/doc/libs/1_54_0/doc/html/variant/reference.html

所以我在我的代码中实现了移动构造函数并禁用了复制构造函数。

当我尝试将某些东西分配给变体对象时,它无法编译。 我尝试了各种不同的方法,包括使用std::move将数据分配给变体对象,但似乎没有什么作用。 根据编译错误堆栈跟踪,我确定问题在variant.hpp中,它正在尝试备份rhs数据。我想知道你们的想法,并让我知道是否正确假设boost variant文档是错误的。

提前致谢。

我正在使用vs2010进行编译,并使用C++11。

这是我的测试代码:

#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include <string>


#pragma warning (push)
#pragma warning (disable: 4127 4244 4265 4503 4512 4640 6011)
#include <boost/optional.hpp>
#include <boost/variant.hpp>
#include <boost/ref.hpp>
#include <boost/shared_ptr.hpp>
#pragma warning (pop)

#include <boost/foreach.hpp>
#include <boost/format.hpp>
using boost::format;
using boost::str;

using namespace std;

class UniqueTest
{
};

class Foo
{
  public:
  std::unique_ptr<UniqueTest> testUniquePtr;

     Foo()      { std::cout << "Foo::Foo\n";  }
     Foo (Foo&& moveData)
     {
     }               

     Foo& operator=(Foo&& moveData)
     {
     return *this;
     }

  private:
     Foo(Foo& tt);
     Foo& operator=(const Foo& tt);

};


int main()
{

  Foo x = Foo();
  boost::variant<std::wstring,Foo> m_result2;
     std::wstring  testString = L"asdf";

  m_result2 = testString; //Fails
  //m_result2 = std::move(testString); //Fails
  //m_result2 = std::move(x); //Fails

  boost::get<Foo>(m_result2).testUniquePtr.get ();
  return 0;
}

1
这不是“禁用拷贝构造函数”的意思 :-S ,而你只是以最广义的意义“实现”了移动构造函数。提示:删除所有手动移动和拷贝构造函数;隐式提供的函数已经足够了。 - Kerrek SB
1
如果您没有用户定义的复制操作或析构函数,移动构造函数将自动生成。删除所有损坏的特殊成员函数,并让编译器正确提供它们。 - Jonathan Wakely
1
@lukesignh,那就是MSVC的一个bug,它可以在符合C++11标准的编译器上编译。 - Jonathan Wakely
1
@DavidBrown,你需要将用户定义的移动操作设置为“noexcept”(因为默认的操作是隐式的)。如果移动操作可能会抛出异常,variant 将不会使用它们,以避免数据丢失,从而提供强大的异常安全保证。 - Jonathan Wakely
1
@lukesignh,我不知道这是否会对VS2010有任何影响,但您可以尝试将移动构造函数设置为“noexcept”,看看是否会使“variant”优先使用它而不是您的私有(因此无法使用)复制构造函数。如果您没有手动定义它,那么它应该是隐式的,但是VS2010早于C++11标准,我不知道它支持多少。 - Jonathan Wakely
显示剩余15条评论
1个回答

4

我的代码能编译通过吗?

不能,variant会尝试调用缺失的复制构造函数。(Foo::Foo(Foo const&)还没有被声明)。

boost/variant/variant.hpp|756 col 9| error: no matching function for call to ‘Foo::Foo(const Foo&)’

即使您声明了它,它也不会起作用:
test.cpp|43 col 6| error: ‘Foo::Foo(const Foo&)’ is private

在评论中已经提到过,但是需要注意的是

  • to make the copy constructor a copy constructor (for good style)

    Foo(Foo const& tt) = delete;
    Foo& operator=(const Foo& tt) = delete;
    
  • to make the move constructor/assignment noexcept, otherwise (like std::vector) variant will refuse to move things because it wouldn't be exception safe.

    Foo(Foo && moveData) noexcept { }    
    Foo& operator=(Foo && moveData) noexcept { return *this; }
    
这是一个在GCC和Clang上编译的简化示例:
#include <iostream>
#include <memory>
#include <string>
#include <boost/variant.hpp>

struct UniqueTest { };

struct Foo
{
public:
    std::unique_ptr<UniqueTest> testUniquePtr;

    Foo() { std::cout << "Foo::Foo\n"; }
    Foo(Foo && moveData) noexcept { }

    Foo& operator=(Foo && moveData) noexcept { return *this; }

    Foo(Foo const& tt) = delete;
    Foo& operator=(const Foo& tt) = delete;
};


int main()
{
    Foo x = Foo();

    boost::variant<std::wstring, Foo> m_result2;

    std::wstring  testString = L"asdf";
    m_result2 = testString; //Fails
    //m_result2 = std::move(testString); //Fails
    //m_result2 = std::move(x); //Fails
    boost::get<Foo>(m_result2).testUniquePtr.get();
}

我刚看到你的帖子。好吧,VS2010不支持nodelete或noexcept关键字。除了自己定义它们之外,我还有其他选择吗? - luke signh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接