C ++ mixin构造函数参数

时间:2011-03-24 23:40:35

标签: c++

我目前正在尝试用C ++实现mixin类。我几乎有我想要的行为。然而,关于传递mixin的基类参数有一个问题:如果基类需要对不可复制对象的const引用,那么代码就无法编译。

正如您在下面的代码示例中看到的那样。类WidgetBase编译正常,MetaWidgetBase得到编译器错误:

#include <iostream>
#include <string>
#include <typeinfo>

class WidgetBase
{
public:
    WidgetBase(WidgetBase * inParent, const std::string & inTitle) :
        mParent(inParent),
        mTitle(inTitle)
    {
    }

    virtual ~WidgetBase() { }

private:
    WidgetBase * mParent;
    std::string mTitle;
};

class MetaWidgetBase
{
public:
    MetaWidgetBase(const std::type_info & inTypeInfo) : mTypeInfo(inTypeInfo) {}

    virtual ~MetaWidgetBase(){}

private:
    const std::type_info & mTypeInfo;
};

template<class SuperType>
class Resizable : public SuperType
{
    typedef SuperType Super;

public:
    void resize(int width, int height) {}

protected:
    Resizable() {}

    template<class Arg0>
    Resizable(Arg0 arg0) : Super(arg0) {}

    template<class Arg0, class Arg1>
    Resizable(Arg0 arg0, Arg1 arg1) : Super(arg0, arg1) {}

    template<class Arg0, class Arg1, class Arg2>
    Resizable(Arg0 arg0, Arg1 arg1, Arg2 arg2) : Super(arg0, arg1, arg2) {}

    template<class Arg0, class Arg1, class Arg2, class Arg3>
    Resizable(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3) : Super(arg0, arg1, arg2, arg3) {}
};

template<class SuperType>
class Movable : public SuperType
{
    typedef SuperType Super;

public:
    void move(int x, int y) {}

protected:
    Movable() {}

    template<class Arg0>
    Movable(Arg0 arg0) : Super(arg0) {}

    template<class Arg0, class Arg1>
    Movable(Arg0 arg0, Arg1 arg1) : Super(arg0, arg1) {}

    template<class Arg0, class Arg1, class Arg2>
    Movable(Arg0 arg0, Arg1 arg1, Arg2 arg2) : Super(arg0, arg1, arg2) {}

    template<class Arg0, class Arg1, class Arg2, class Arg3>
    Movable(Arg0 arg0, Arg1 arg1, Arg2 arg2, Arg3 arg3) : Super(arg0, arg1, arg2, arg3) {}
};


class MyWidget : public Resizable<Movable<WidgetBase> >
{
    typedef Resizable<Movable<WidgetBase> > Super;

public:
    MyWidget(WidgetBase * inParent, const std::string & inTitle) :
        Super(inParent, inTitle)
    {
    }
};


class MyMetaWidget : public Resizable<MetaWidgetBase>
{
    typedef Resizable<MetaWidgetBase> Super;

public:
    MyMetaWidget() :
        Super(typeid(this))
    {
    }
};

int main()
{
    // Works fine:
    MyWidget widget(NULL, "Hello");

    // Error: std::type_info has private copy constructor!
    MyMetaWidget metaWidget;

    return 0;
}

确切的错误信息是:

/usr/include/c++/4.2.1/typeinfo: In constructor 'MyMetaWidget::MyMetaWidget()':
/usr/include/c++/4.2.1/typeinfo:135: error: 'std::type_info::type_info(const std::type_info&)' is private

我目前的解决方法是将std :: type_info对象作为const指针而不是const引用传递。

有没有人如何妥善处理这个问题?

更新

似乎在转发期间,模板参数只会松散其类型签名的引用部分。其余的都没有受到伤害。这也意味着如果我们使用“ArgN&amp;”作为参数类型,我们是安全的。

所以基本上解决方案是这样的:让mixin类的构造函数将其所有参数作为非const引用。

这是我的测试各种组合(GCC)的示例应用程序:

#include <iostream>
#include <string>
#include <typeinfo>

#define TRACE std::cout << __PRETTY_FUNCTION__ << std::endl << std::flush;
#define TRACE_MIXIN std::cout << __PRETTY_FUNCTION__ << std::endl << std::flush;
#define TRACE_BASE std::cout << "\n" << __PRETTY_FUNCTION__ << std::endl << std::flush;

// Savable is a mixin class for saving object state to file.
template<class BaseType>
struct Savable : public BaseType {
    Savable() {
        TRACE_MIXIN
    }

    template<class Arg0>
    Savable(Arg0 & arg0) : BaseType(arg0) {
        TRACE_MIXIN
    }

    template<class Arg0, class Arg1>
    Savable(Arg0 & arg0, Arg1 & arg1) : BaseType(arg0, arg1) {
        TRACE_MIXIN
    }

    void Save(const std::string & inFileName) {}
};

// Loadable is a mixin class for load object state from file.
template<class BaseType>
struct Loadable : public BaseType {
    Loadable() {
        TRACE_MIXIN
    }

    template<class Arg0>
    Loadable(Arg0 & arg0) : BaseType(arg0) {
        TRACE_MIXIN
    }

    template<class Arg0, class Arg1>
    Loadable(Arg0 & arg0, Arg1 & arg1) : BaseType(arg0, arg1) {
        TRACE_MIXIN
    }

    void Load(const std::string & inFileName) {}
};

// Test with a copyable constructor args
namespace CopyLand {

struct MyObject  {}; // copyable

struct ValueBase {
    ValueBase(MyObject inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    MyObject mObject;
    std::string mTest;
};

struct ConstValueBase {
    ConstValueBase(const MyObject inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    const MyObject mObject;
    std::string mTest;
};

struct PtrBase {
    PtrBase(MyObject * inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    MyObject * mObject;
    std::string mTest;
};

struct ConstPtrBase {
    ConstPtrBase(const MyObject * inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    const MyObject * mObject;
    std::string mTest;
};

struct RefBase {
    RefBase(MyObject & inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    MyObject & mObject;
    std::string mTest;
};

struct ConstRefBase {
    ConstRefBase(const MyObject & inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    const MyObject & mObject;
    std::string mTest;
};

struct LoadableAndSavableValueBase : public Loadable<Savable<ValueBase> > {
    LoadableAndSavableValueBase(MyObject inObject, const std::string & inTest) :
        Loadable<Savable<ValueBase> >(inObject, inTest) {
        TRACE
    }
};

struct LoadableAndSavableConstValueBase : public Loadable<Savable<ConstValueBase> > {
    LoadableAndSavableConstValueBase(const MyObject inObject, const std::string & inTest) :
        Loadable<Savable<ConstValueBase> >(inObject, inTest) {
        TRACE
    }
};

struct LoadableAndSavablePtrBase : public Loadable<Savable<PtrBase> > {
    LoadableAndSavablePtrBase(MyObject * inObject, const std::string & inTest) :
        Loadable<Savable<PtrBase> >(inObject, inTest) {
        TRACE
    }
};

struct LoadableAndSavableRefBase : public Loadable<Savable<RefBase> > {
    LoadableAndSavableRefBase(MyObject & inObject, const std::string & inTest) :
        Loadable<Savable<RefBase> >(inObject, inTest) {
        TRACE
    }
};

struct LoadableAndSavableConstPtrBase : public Loadable<Savable<ConstPtrBase> > {
    LoadableAndSavableConstPtrBase(const MyObject * inObject, const std::string & inTest) :
        Loadable<Savable<ConstPtrBase> >(inObject, inTest) {
        TRACE
    }
};

struct LoadableAndSavableConstRefBase : public Loadable<Savable<ConstRefBase> > {
    LoadableAndSavableConstRefBase(const MyObject & inObject, const std::string & inTest) :
        Loadable<Savable<ConstRefBase> >(inObject, inTest) {
        TRACE
    }
};

void test() {
    MyObject myObject;
    LoadableAndSavableValueBase valueBase(myObject, "Test");
    LoadableAndSavableConstValueBase constValueBase(myObject, "Test");
    LoadableAndSavablePtrBase ptrBase(&myObject, "Test");
    LoadableAndSavableConstPtrBase constPtrBase(&myObject, "Test");
    LoadableAndSavableRefBase refBase(myObject, "Test");
    LoadableAndSavableConstRefBase constRefBase(myObject, "Test");
}

} // namespace CopyLand

namespace NoCopyLand { // Test with a noncopyable Arg0 object

struct MyObject {
    MyObject() {}

    // noncopyable
    MyObject(const MyObject&);
    const MyObject& operator=(const MyObject&);
};

struct PtrBase {
    PtrBase(MyObject * inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    MyObject * mObject;
    std::string mTest;
};

struct ConstPtrBase {
    ConstPtrBase(const MyObject * inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    const MyObject * mObject;
    std::string mTest;
};

struct RefBase {
    RefBase(MyObject & inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    MyObject & mObject;
    std::string mTest;
};

struct ConstRefBase {
    ConstRefBase(const MyObject & inObject, const std::string & inTest) :
        mObject(inObject), mTest(inTest) {
        TRACE_BASE
    }
    const MyObject & mObject;
    std::string mTest;
};

struct LoadableAndSavablePtrBase : public Loadable<Savable<PtrBase> > {
    LoadableAndSavablePtrBase(MyObject * inObject, const std::string & inTest) :
        Loadable<Savable<PtrBase> >(inObject, inTest)  {
        TRACE
    }
};

struct LoadableAndSavableRefBase : public Loadable<Savable<RefBase> > {
    LoadableAndSavableRefBase(MyObject & inObject, const std::string & inTest) :
        Loadable<Savable<RefBase> >(inObject, inTest)  {
        TRACE
    }
};

struct LoadableAndSavableConstPtrBase : public Loadable<Savable<ConstPtrBase> > {
    LoadableAndSavableConstPtrBase(const MyObject * inObject, const std::string & inTest) :
        Loadable<Savable<ConstPtrBase> >(inObject, inTest)  {
        TRACE
    }
};

struct LoadableAndSavableConstRefBase : public Loadable<Savable<ConstRefBase> > {
    LoadableAndSavableConstRefBase(const MyObject & inObject, const std::string & inTest) :
        Loadable<Savable<ConstRefBase> >(inObject, inTest)  {
        TRACE
    }
};

// Also include a test with std::type_info.
struct TypeInfo {
    TypeInfo(const std::type_info & inTypeInfo) : mTypeInfo(inTypeInfo) {}

    const std::type_info & mTypeInfo;
};

struct LoadableAndSavableTypeInfo : public Loadable<Savable<TypeInfo> > {
    LoadableAndSavableTypeInfo() : Loadable<Savable<TypeInfo> >(typeid(this)) {}
};

void test() {
    MyObject myObject;
    LoadableAndSavablePtrBase ptrBase(&myObject, "Test");
    LoadableAndSavableConstPtrBase constPtrBase(&myObject, "Test");
    LoadableAndSavableRefBase refBase(myObject, "Test");
    LoadableAndSavableConstRefBase constRefBase(myObject, "Test");
    LoadableAndSavableTypeInfo typeInfo;
    typeInfo.mTypeInfo.name(); // get the std::type_info
}

} // namespace NoCopyLand

int main() {
    CopyLand::test();
    NoCopyLand::test();
    return 0;
}

运行程序时,它会产生以下输出:

g++ -o test main.cpp

CopyLand::ValueBase::ValueBase(CopyLand::MyObject, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = CopyLand::ValueBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<CopyLand::ValueBase>]
CopyLand::LoadableAndSavableValueBase::LoadableAndSavableValueBase(CopyLand::MyObject, const std::string&)

CopyLand::ConstValueBase::ConstValueBase(CopyLand::MyObject, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = const CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = CopyLand::ConstValueBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = const CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<CopyLand::ConstValueBase>]
CopyLand::LoadableAndSavableConstValueBase::LoadableAndSavableConstValueBase(CopyLand::MyObject, const std::string&)

CopyLand::PtrBase::PtrBase(CopyLand::MyObject*, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = CopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = CopyLand::PtrBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = CopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<CopyLand::PtrBase>]
CopyLand::LoadableAndSavablePtrBase::LoadableAndSavablePtrBase(CopyLand::MyObject*, const std::string&)

CopyLand::ConstPtrBase::ConstPtrBase(const CopyLand::MyObject*, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = const CopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = CopyLand::ConstPtrBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = const CopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<CopyLand::ConstPtrBase>]
CopyLand::LoadableAndSavableConstPtrBase::LoadableAndSavableConstPtrBase(const CopyLand::MyObject*, const std::string&)

CopyLand::RefBase::RefBase(CopyLand::MyObject&, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = CopyLand::RefBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<CopyLand::RefBase>]
CopyLand::LoadableAndSavableRefBase::LoadableAndSavableRefBase(CopyLand::MyObject&, const std::string&)

CopyLand::ConstRefBase::ConstRefBase(const CopyLand::MyObject&, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = const CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = CopyLand::ConstRefBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = const CopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<CopyLand::ConstRefBase>]
CopyLand::LoadableAndSavableConstRefBase::LoadableAndSavableConstRefBase(const CopyLand::MyObject&, const std::string&)

NoCopyLand::PtrBase::PtrBase(NoCopyLand::MyObject*, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = NoCopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = NoCopyLand::PtrBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = NoCopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<NoCopyLand::PtrBase>]
NoCopyLand::LoadableAndSavablePtrBase::LoadableAndSavablePtrBase(NoCopyLand::MyObject*, const std::string&)

NoCopyLand::ConstPtrBase::ConstPtrBase(const NoCopyLand::MyObject*, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = const NoCopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = NoCopyLand::ConstPtrBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = const NoCopyLand::MyObject*, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<NoCopyLand::ConstPtrBase>]
NoCopyLand::LoadableAndSavableConstPtrBase::LoadableAndSavableConstPtrBase(const NoCopyLand::MyObject*, const std::string&)

NoCopyLand::RefBase::RefBase(NoCopyLand::MyObject&, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = NoCopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = NoCopyLand::RefBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = NoCopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<NoCopyLand::RefBase>]
NoCopyLand::LoadableAndSavableRefBase::LoadableAndSavableRefBase(NoCopyLand::MyObject&, const std::string&)

NoCopyLand::ConstRefBase::ConstRefBase(const NoCopyLand::MyObject&, const std::string&)
Savable<BaseType>::Savable(Arg0&, Arg1&) [with Arg0 = const NoCopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = NoCopyLand::ConstRefBase]
Loadable<BaseType>::Loadable(Arg0&, Arg1&) [with Arg0 = const NoCopyLand::MyObject, Arg1 = const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, BaseType = Savable<NoCopyLand::ConstRefBase>]
NoCopyLand::LoadableAndSavableConstRefBase::LoadableAndSavableConstRefBase(const NoCopyLand::MyObject&, const std::string&)
Savable<BaseType>::Savable(Arg0&) [with Arg0 = const std::type_info, BaseType = NoCopyLand::TypeInfo]
Loadable<BaseType>::Loadable(Arg0&) [with Arg0 = const std::type_info, BaseType = Savable<NoCopyLand::TypeInfo>]

如果我的例子或结论中有任何错误,请告诉我。)

1 个答案:

答案 0 :(得分:2)

您在mixin类中按值ArgN。您可以尝试Movable(Arg0 const& arg0, Arg1 const& arg1),同样也可以Resizable