函数参数分组和常量

时间:2011-11-22 09:48:25

标签: c++ templates const

我们正在尝试重构我们的代码,我们想要的改进之一是:许多函数有很多参数,但其中许多共享一个共同的子集。因此,我们希望创建一个将它们分组的结构。问题是有些函数需要一些参数是const而有些则不是。其中一些函数必须能够调用这些函数的子集来提供此参数分组结构,但是受以下限制:被调用的函数不能“降级”此结构的常量(请参阅下面的示例)。实现此结构的所有必需变体解决了问题但不优雅。我们正在研究的一个解决方案是使用模板,例如:

template<class A, class B, class C>
struct my_container
{
    A a;
    B b;
    C c;
};

void foo1(my_container<int, char, float const> & my_cont)
{
}

void foo2(my_container<int const, char, float const> & my_cont)
{
    // This should NOT be allowed: we do mind something being const to be treated by the
    // called function as non-const.
    foo1(my_cont);
}

void foo3(my_container<int, char, float> & my_cont)
{
    // This should be allowed: we don't mind something being non-const to be treated by the
    // called function as const.
    foo2(my_cont);
}

我们的问题是foo2在没有编译器抱怨的情况下调用foo1,我们想要完全相反。这甚至可以用模板实现吗?还有其他技术吗?

3 个答案:

答案 0 :(得分:1)

两者都不应该奏效。模板的不同实例化是 未删除的类型,它们之间没有隐式转换。所以 my_container<int, char, float const>my_container<int const, char, float const>my_container<int, char, float>都是无关的 类型,它们之间没有隐式转换。

应该可以使用继承来计算一些东西 元编程技巧来确定你继承的是什么,但我不是 确定如何,我怀疑这将是更多的努力而不是它的价值。

答案 1 :(得分:1)

不依赖于未定义的行为,这可以通过另一个间接层来实现。添加引用原始成员的视图类。可以隐式添加常量,但不能删除。

template<class A, class B, class C>
struct my_container
{
    A a;
    B b;
    C c;
};

template <class A, class B, class C>
class my_container_view
{
    A* a_;
    B* b_;
    C* c_;

public:
    template <class A_, class B_, class C_>
    my_container_view(my_container<A_, B_, C_>& source):
        a_(&source.a), b_(&source.b), c_(&source.c)
    {}
    template <class A_, class B_, class C_>
    my_container_view(my_container_view<A_, B_, C_>& source):
        a_(&source.a()), b_(&source.b()), c_(&source.c())
    {}
    A& a() const { return *a_; }
    B& b() const { return *b_; }
    C& c() const { return *c_; }
};

void foo1(my_container_view<int, char, float const> my_cont)
{
    my_cont.a() = 10;
    my_cont.b() = 'a';
    my_cont.c() /*= 3.14*/;
}

void foo2(my_container_view<int const, char, float const> my_cont)
{
    my_cont.a() /*= 10*/;
    my_cont.b() = 'a';
    my_cont.c() /*= 3.14*/;

    //foo1(my_cont); //not allowed
}

void foo3(my_container_view<int, char, float> my_cont)
{
    my_cont.a() = 10;
    my_cont.b() = 'a';
    my_cont.c() = 3.14;
t
    foo2(my_cont);
}

int main()
{
    my_container<int, char, float> mc;
    foo1(mc);
    foo2(mc);
    foo3(mc);
}

(但我怀疑,这是多少值得。通常在课程中,你可以修改其所有成员 - 你只是不修改你不想要的那些 - 或者你可以'修改任何。如果你想要这个级别的控制,你宁愿单独传递每个参数 - 与你正在做的相反。)

答案 2 :(得分:0)

我的解决方案有点元编程。看起来很丑陋并没有经过全面测试,但无论如何: 这样,一切都应该“开箱即用”

#include <iostream>
#include <boost/type_traits.hpp>

template<class A, class B, class C>
struct my_container
{
    A a;
    B b;
    C c;

    template<typename An, typename Bn, typename Cn>
    operator my_container<An,Bn,Cn>& ()
    {
        /* First, check whether compatible at all */
        BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<A>::type, typename boost::remove_cv<An>::type>::value));
        BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<B>::type, typename boost::remove_cv<Bn>::type>::value));
        BOOST_STATIC_ASSERT((boost::is_same<typename boost::remove_cv<C>::type, typename boost::remove_cv<Cn>::type>::value));
        /* Enforce const'ness */
        BOOST_STATIC_ASSERT( !boost::is_const<A>::value || boost::is_const<An>::value );
        BOOST_STATIC_ASSERT( !boost::is_const<B>::value || boost::is_const<Bn>::value );
        BOOST_STATIC_ASSERT( !boost::is_const<C>::value || boost::is_const<Cn>::value );

        return *reinterpret_cast< my_container<An,Bn,Cn>* >(this);
    }
};

void foo1(my_container<int, char, float const> & my_cont)
{
}

void foo2(my_container<int const, char, float const> & my_cont)
{
    // This should NOT be allowed: we do mind something being const to be treated by the
    // called function as non-const.
    //foo1(my_cont); /// BOOST_STATIC_ASSERT fails! Hurray!
}

void foo3(my_container<int, char, float> & my_cont)
{
    // This should be allowed: we don't mind something being non-const to be treated by the
    // called function as const.
    foo2(my_cont); /// No complaints! Hurray!
}

int main(int argc, char* argv[])
{
    my_container<int,char,float> foobar;

    foo3(foobar);

    return 0;
}