【问题标题】:Function arguments grouping and constness函数参数分组和常量
【发布时间】:2011-11-22 09:48:24
【问题描述】:

我们正在尝试重构我们的代码,我们想要的改进之一如下:许多函数有许多参数,但其中许多共享一个公共子集。因此,我们想创建一个将它们分组的结构。问题是有些函数需要一些参数是 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,而我们希望完全相反。这甚至可以用模板实现吗?有没有其他技术?

【问题讨论】:

  • 在我看来,如果 constness 因人而异,也许只是不要尝试对这些论点进行分组。
  • 该模板似乎表明您试图将三个相当不相关的值混为一谈? - 如果您有一个真正的相关功能类,我认为通常不会尝试使其部分可修改。例如,您不会特意让函数只修改 Point 实例的 x 坐标而不是 y。如果它很重要,我猜你会将它们作为单独的参数传递。

标签: c++ templates constants


【解决方案1】:

都不应该工作。模板的不同实例是 不相关的类型,它们之间没有隐式转换。所以 my_container&lt;int, char, float const&gt;my_container<int const, char, float const>my_container&lt;int, char, float&gt; 均不相关 类型,它们之间没有隐式转换。

应该可以使用继承来解决问题,使用 元编程技巧来确定你继承什么,但我不是 确定如何,我怀疑这会比它值得付出更多的努力。

【讨论】:

  • 你是对的,但我还是很快测试了它。正如预期的那样,两者都不适用于 g++ 4.4。或 MSVC 2010。
【解决方案2】:

在不涉及未定义行为的情况下,这可以通过另一个级别的间接来实现。添加一个引用原始成员的视图类。常量可以隐式添加,但不能删除。

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

(不过,我怀疑这值多少钱。通常对于类,您可以修改它的所有成员 - 而你只是不要修改你不想修改的成员 - 或者你可以' t 修改任何。如果你想要这种级别的控制,你宁愿单独传递每个参数 - 与你正在做的相反。)

【讨论】:

  • 我想了解您的解决方案与我的相比有什么好处,除了您的不需要提升?关于您的言论,我认为 OP 在他的问题的前两句话中明确了他的用例。
  • @zerm:我怀疑技术上不相关的类型之间的这种 reinterpret_casting 在技术上是未定义的行为,尽管我不明白为什么它在实践中不起作用。 - 至于疑问:“问题是有些函数需要一些参数是 const 而有些不是。” - 如果生成的对象真的形成一个整体,我还没有遇到过这样的困境。或者,如果您想要更细粒度的 constness 控制 - 分别传递每个成员,因此每个成员都可以拥有自己的 constness。
  • reinterpret_cast 的优点是,我修复了我的解决方案,因为两种类型都需要相同(除了 const'ness 之外)并且不仅可转换——这样我看不出 reinterpret_cast 可能导致任何问题的原因吗?我会检查标准的内容。
【解决方案3】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-11-25
    • 2015-09-10
    • 1970-01-01
    • 2021-11-03
    • 2016-04-24
    • 2015-03-19
    • 1970-01-01
    相关资源
    最近更新 更多