C++Opjor或UnQuyJPTR:用CLAN和GNU LIGBSTDC++来构造构造函数引用参数类型 你好,C++程序员!

C++Opjor或UnQuyJPTR:用CLAN和GNU LIGBSTDC++来构造构造函数引用参数类型 你好,C++程序员!,c++,templates,variant,clang++,C++,Templates,Variant,Clang++,我试图编写一个C++变体类来模拟一个粘性类型的联盟,而不受MsS2012的限制,因为它缺少标准。我知道C++有很多变种实现,但重新发明的东西很有趣。不管怎样,学术界对潜在问题也有一些兴趣 以下是代码-由于两点原因,对我来说有点过于复杂: 这是针对MSVC2012的,它缺少C++11标准中用于此类任务的一些最关键的部分,如可变模板或构造函数继承 我没有找到一种方法来创建专用的复制/移动构造函数,而不使用模板化的变体创建模糊的重载。我曾尝试进行std::enable_if检查,以防止派生/相同类型的

我试图编写一个C++变体类来模拟一个粘性类型的联盟,而不受MsS2012的限制,因为它缺少标准。我知道C++有很多变种实现,但重新发明的东西很有趣。不管怎样,学术界对潜在问题也有一些兴趣

以下是代码-由于两点原因,对我来说有点过于复杂:

这是针对MSVC2012的,它缺少C++11标准中用于此类任务的一些最关键的部分,如可变模板或构造函数继承 我没有找到一种方法来创建专用的复制/移动构造函数,而不使用模板化的变体创建模糊的重载。我曾尝试进行std::enable_if检查,以防止派生/相同类型的模板构造函数生成为变量本身,但它似乎阻止了MSVC隐式转换。因此,我移动了派生类变量本身的检查copy/move,而不是通过将其中一个类型复制/移动到helper结构中进行初始化。 下面的代码是一个没有访问器的略显简短的版本。为了减少噪音,不管怎样,它们都是微不足道的,一些检查和名称空间。这个问题在下面的代码中有更详细的解释,虽然它在MSVC上的工作令人惊讶地完美无瑕,但在gcc-4.9.2中GNU实现的clang和libstdc++STL库中并没有出现。只有当我试图在变量中放入惟一的_ptr并将其放入std::vector中时

#include <memory>
#include <vector>
#include <utility>

// A number of template tricks to emulate tuple query things still missing from MSVC2012. Omitted for clarity, nothing really special there - looping through tuples and checking types with is_conversible
#include "core_template_magic.hpp"

template <typename TypesTuple>
class Variant
{
public:
    typedef Variant<TypesTuple> VariantBase;

    template <typename T>
    struct type_of
    {
        enum { value = find_type_in_tuple<T, TypesTuple>::value };
        static_assert(value >= 0, "There is no such type in this variant");
    };

public:
    template <typename T>
    Variant(const T& value) :
        type_(get_constructor_type<T>::run(value))
    {
        construct_copy<T, 0>::run(mem_, value);
    }

    template <typename T>
    Variant(T&& value) :
        type_(get_constructor_type<T>::run(value))
    {
        construct_move<T, 0>::run(mem_, std::forward<T>(value));
    }

    ~Variant()
    {
        destroy<0>::run(mem_, type_);
    }

private:
    /* a wrapper around type_of to work around copy constructors */

    template <typename T>
    struct is_self : public std::is_base_of<VariantBase, T> {};

    template <typename T, bool is_self = is_self<T>::value>
    struct get_constructor_type {};

    template <typename T>
    struct get_constructor_type<T, false>
    {
        static int run(const T&)
        {
            return type_of<T>::value;
        }

        static int run(T&&)
        {
            return type_of<T>::value;
        }
    };

    template <typename T>
    struct get_constructor_type<T, true>
    {
        static int run(const VariantBase& other)
        {
            return other.type_;
        }

        static int run(VariantBase&& other)
        {
            return other.type_;
        }
    };

    /* a helper for the copy constructor */

    template <typename T, int i,
                int count = std::tuple_size<TypesTuple>::value,
                bool is_self = is_self<T>::value>
    struct construct_copy
    {
        static void run(char* mem, const T& other)
        {
            new (mem) T(other);
        }
    };

    // If that's an another variant of same type - find out it's type
    // and call an appropriate copy constructor
    template <typename T, int i, int count>
    struct construct_copy<T, i, count, true>
    {
        static void run(char* mem, const VariantBase& other)
        {
            typedef typename std::tuple_element<i, TypesTuple>::type E;

            if (other.type_ == i)
                new (mem) E(*reinterpret_cast<const E*>(other.mem_));
            else
                construct_copy<T, i + 1, count, true>::run(mem, other);
        }
    };

    template <typename T, int count>
    struct construct_copy<T, count, count, true>
    { static void run(char* mem, const VariantBase& other) {} };

    /* a helper for the move constructor */

    template <typename T, int i,
                int count = std::tuple_size<TypesTuple>::value,
                bool is_self = is_self<T>::value>
    struct construct_move
    {
        static void run(char* mem, T&& other)
        {
            new (mem) T(std::forward<T>(other));
        }
    };

    // If that's an another variant of same type - find out it's type
    // and call an appropriate  move constructor
    template <typename T, int i, int count>
    struct construct_move<T, i, count, true>
    {
        static void run(char* mem, VariantBase&& other)
        {
            typedef typename std::tuple_element<i, TypesTuple>::type E;

            if (other.type_ == i)
                new (mem) E(std::move(*reinterpret_cast<E*>(other.mem_)));
            else
                construct_move<T, i + 1, count, true>::run(mem, std::forward<VariantBase>(other));
        }
    };

    template <typename T, int count>
    struct construct_move<T, count, count, true>
    { static void run(char* mem, VariantBase&& other) {} };

    // A destructor helper

    template <int i, int count = std::tuple_size<TypesTuple>::value>
    struct destroy
    {
        static void run(char* mem, const int type)
        {
            typedef typename std::tuple_element<i, TypesTuple>::type T;
            if (type == i)
                reinterpret_cast<T*>(mem)->~T();
            else
                destroy<i + 1, count>::run(mem, type);
        }
    };

    template <int count>
    struct destroy<count, count>
    { static void run(char* mem, const int type) {} };

private:
    const int type_;
    char mem_[get_max_sizeof_of_types<TypesTuple>::value];
};

// example problematic usage

struct Test : public Variant<std::tuple<
    std::unique_ptr<int>,
    std::unique_ptr<bool>,
    std::unique_ptr<double>
>>
{
    using VariantBase::VariantBase;
};

int main(int argc, char *argv[])
{
    std::vector<Test> v;

    v.emplace_back(std::move(std::unique_ptr<int>(new int)));

    return 0;
}
所以,当我试图用libstdc++-4.9.2在clang++-3.6.0上编译它时,我得到了一个错误

../main.cpp:58:14: error: multiple overloads of 'run' instantiate to the same signature 'int (Test &&)'
                static int run(T&&)
                           ^
../main.cpp:31:9: note: in instantiation of template class 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::get_constructor_type<Test &, false>' requested here
                type_(get_constructor_type<T>::run(value))
                      ^
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
        using VariantBase::VariantBase;
                           ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
      return std::__uninitialized_copy_a
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
              = std::__uninitialized_move_if_noexcept_a
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
          _M_emplace_back_aux(std::forward<_Args>(__args)...);
          ^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
        v.emplace_back(std::move(std::unique_ptr<int>(new int)));
          ^
../main.cpp:53:14: note: previous declaration is here
                static int run(const T&)
                           ^
../main.cpp:18:3: error: static_assert failed "There is no such type in this variant"
                static_assert(value >= 0, "There is no such type in this variant");
                ^             ~~~~~~~~~~
../main.cpp:60:11: note: in instantiation of template class 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::type_of<Test &>' requested here
                        return type_of<T>::value;
                               ^
../main.cpp:31:34: note: in instantiation of member function 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::get_constructor_type<Test &, false>::run' requested here
                type_(get_constructor_type<T>::run(value))
                                               ^
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
        using VariantBase::VariantBase;
                           ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
      return std::__uninitialized_copy_a
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
              = std::__uninitialized_move_if_noexcept_a
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
          _M_emplace_back_aux(std::forward<_Args>(__args)...);
          ^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
        v.emplace_back(std::move(std::unique_ptr<int>(new int)));
          ^
../main.cpp:120:14: error: cannot allocate reference type 'Test &' with new
                        new (mem) T(std::forward<T>(other));
                                  ^
../main.cpp:33:25: note: in instantiation of member function 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::construct_move<Test &, 0, 3, false>::run' requested here
                construct_move<T, 0>::run(mem_, std::forward<T>(value));
                                      ^
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
        using VariantBase::VariantBase;
                           ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
      return std::__uninitialized_copy_a
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
              = std::__uninitialized_move_if_noexcept_a
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
          _M_emplace_back_aux(std::forward<_Args>(__args)...);
          ^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
        v.emplace_back(std::move(std::unique_ptr<int>(new int)));
          ^

注意,踪迹中间的某处消失,并被测试和替换。我的理解是,emplace_back工作正常,并且在

/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
          _M_emplace_back_aux(std::forward<_Args>(__args)...);
该项已成功地按其应该的方式构造到变量中,但出于某种原因,它似乎尝试了某种未初始化的\u副本,即新项,再次调用复制/移动构造函数,但这次使用了非常量引用。为什么emplace_back会复制

显然,我的fabolus检查同一类型,即检测整个变体复制/移动,试图触发更复杂的“找出我们是什么类型”逻辑,但在收到引用类型时失败。嗯,这是合乎逻辑的

好吧,我已经尝试通过在支票中放置一个腐烂的类型来破解这个问题

template <typename T>
struct is_self : public std::is_base_of<VariantBase, typename std::decay<T>::type> {};
。。。但是没有用

../main.cpp:33:35: error: rvalue reference to type 'VariantBase' (aka 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >') cannot bind to lvalue of type 'Test'
                construct_move<T, 0>::run(mem_, std::forward<T>(value));
                                                ^~~~~~~~~~~~~~~~~~~~~~
../main.cpp:174:21: note: in instantiation of function template specialization 'Variant<std::tuple<std::unique_ptr<int, std::default_delete<int> >, std::unique_ptr<bool, std::default_delete<bool> >, std::unique_ptr<double, std::default_delete<double> > > >::Variant<Test &>' requested here
        using VariantBase::VariantBase;
                           ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:75:8: note: in instantiation of function template specialization 'std::_Construct<Test, Test &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:125:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<Test *, Test *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:278:19: note: in instantiation of function template specialization 'std::uninitialized_copy<Test *, Test *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/stl_uninitialized.h:299:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<Test *, Test *, Test>' requested here
      return std::__uninitialized_copy_a
                  ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:421:15: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<Test *, Test *, std::allocator<Test> >' requested here
              = std::__uninitialized_move_if_noexcept_a
                     ^
/usr/lib/gcc/x86_64-pc-linux-gnu/4.9.2/include/g++-v4/bits/vector.tcc:101:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::_M_emplace_back_aux<std::unique_ptr<int, std::default_delete<int> > >' requested here
          _M_emplace_back_aux(std::forward<_Args>(__args)...);
          ^
../main.cpp:181:4: note: in instantiation of function template specialization 'std::vector<Test, std::allocator<Test> >::emplace_back<std::unique_ptr<int, std::default_delete<int> > >' requested here
        v.emplace_back(std::move(std::unique_ptr<int>(new int)));
          ^
../main.cpp:129:44: note: passing argument to parameter 'other' here
                static void run(char* mem, VariantBase&& other)
嗯,这也是合乎逻辑的。当然,我可能会在这里对引用类型执行一些更复杂的检查,但这只会增加已经很复杂的代码,更重要的是,我不清楚我将如何处理左值引用-将其转换为右值并使用移动语义处理,也许

因此,我的问题是:

我做错了吗?有没有更简单的方法,可能是使用更显式的函数?或者我应该使用非限定的模板类型模板,然后检查常量复制vs移动、引用和类型? 向量中发生了什么,对这些事情的正确反应是什么?
据我所知,这是由于类和move_if_noexcept之间的一些奇怪的交互导致libstdc++的向量试图复制您的变量,这反过来又破坏了一切。一个简单的黑客解决方案是将您的T&&构造函数标记为noexcept。好的,是的,它可以工作,谢谢!无论如何,我不使用异常,通常我也不会费心放置noexcept之类的东西,所以这对我来说是可以接受的。但问题的学术部分仍然存在——为什么会发生这种情况?我想我会用你的方向提示看一下STL代码。