函数参数分组和constness

Function arguments grouping and constness

本文关键字:constness 参数 函数      更新时间:2023-10-16

我们正在尝试重构我们的代码,我们想要的改进之一是:许多函数有许多参数,但其中许多函数共享一个公共子集。所以,我们想要创建一个结构来对它们进行分组。问题是,有些函数需要一些形参为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时,编译器不会报错,而我们希望正好相反。这有可能用模板实现吗?还有其他的技巧吗?

两者都不行。模板的不同实例是不相关的类型,它们之间没有隐式转换。所以my_container<int, char, float const>, my_container<int const, char, float const>my_container<int, char, float>都是不相关的类型,它们之间没有隐式转换。

应该可以使用继承、使用元编程技巧来确定继承的对象,但我不是当然可以,而且我怀疑这样做会得不偿失。

在不与未定义行为接壤的情况下,这可以通过另一层间接实现。添加一个引用原始成员的视图类。Constness可以隐式添加,但不能删除。

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);
}
我有我的怀疑,虽然,这到底值多少钱。通常对于类,要么你可以修改它的所有成员——你只是不修改那些你不想修改的成员——要么你不能修改任何成员。如果您想要这种级别的控制,您宁愿单独传递每个参数—与您所做的相反。

我的解决方案是一点元编程。看起来很丑,没有经过全面测试,但无论如何:这样,一切都应该正常运行

#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;
}