【问题标题】:Hiding argument type from the user in C++在 C++ 中向用户隐藏参数类型
【发布时间】:2014-08-03 04:58:44
【问题描述】:

假设我正在编写一个容器类template <typename T> class MyContainer,出于某种原因,我想传递T 类型的对象,并且它们的类型对用户隐藏。特别是我想确保对MyContainer::Foo 的任何输入都首先通过MyContainer::HideType

template <typename T>
class MyContainer {
 public:
  ... constructors and stuff...

  HidesType& HideType(T&);      
  T& UnhideType(HidesType&);

  void Foo(HideType&);

  ... some other stuff...
};

现在我想知道 HidesType 可以/应该是什么。一些选项是:

struct HidesType { 
  HidesType(T& data) : data_(data) { }
  T& data_; 
};
HidesType HideType(T& data) { return HidesType(data); }

union HidesType { T data_; };
HidesType& HideType(T& data) { return reinterpret_cast<HidesType&>(data); }

class HidesType : public T { };
HidesType& HideType(T& data) { return static_cast<HidesType&>(data); }

第一个问题是用户不能将T对象保留为

MyContainer<T>::HidesType tmp(HideType(t_obj));

因为一旦t_obj 超出范围,tmp 就会失效。另外,我不确定编译器是否会完全优化 HidesType。

第二个的问题是我不知道 reinterpret_cast 是否安全(例如不会有任何对齐问题吗?)。

第三个问题是 T 可能被标记为 final 并且 HidesType& 隐式转换为 T&。

任何想法或建议将不胜感激。

编辑:

隐藏的主要目的是使HidesType抽象并与T区分开来。 解释导致这个问题的所有背景会很麻烦。现在,让我们假设函数MyContainer::Foo 本质上将T 作为输入,但是,我不希望用户知道/使用它,特别是接口可能在未来发生变化。

正确的用例可能是:

MyContainer<T>::HidesType handle = MyContainer<T>::HideType(t_obj);
... do something ...
... perhaps t_obj.~T(); ...
... do something ...
my_container.Foo(handle);

无效的用例:

MyContainer<T>::HidesType handle = MyContainer<T>::HideType(t_obj);
... do something ...
my_container.Foo(some_other_t_obj);

【问题讨论】:

  • 这种隐藏的目的是什么,具体在什么情况下该类型应该“隐藏”,“隐藏”是什么意思?
  • 我认为客户端代码不可能实现的示例将有助于澄清事情。
  • 鉴于用户首先提供了模板类型,我不确定您是否可以对用户隐藏模板类型。
  • @Cheersandhth.-Alf 查看编辑。隐藏的主要目的是使HidesType抽象并与T区分开来。
  • @Galik 我想隐藏的不是T 本身,而是MyContainer::FooT 作为参数这一事实。

标签: c++ c++11 casting type-conversion


【解决方案1】:

您似乎想要一个类型 H,它只能由 MyContainer&lt;T&gt; 实例化或修改。

实现这一点的简单方法是使用private 构造函数和赋值运算符(移动构造函数除外)并将MyContainer&lt;T&gt; 作为friend 的类类型。

您可以将move 指定的T 对象放入其中,既是为了提高效率,也是为了避免不必要地要求它是可复制的。如果是这样,那么H 将不保证是可复制的。但在我看来,它应该是可移动的。

【讨论】:

  • 查看对 Jarod42 答案的评论。我正在尝试实现零成本抽象,即最终代码应该看起来像 HidesType 只是 T 的 typedef。我不太确定编译器是否可以优化所有这些。
  • @dtldarek:您必须更详细地描述您所看到的成本。我肯定没有看到没有成本(除了可能复制T 对象之外)。无论如何,这有点像过早的优化。优化冲动的答案是MEASURE。当优化冲动出现时,总是这样做。;-)
【解决方案2】:

你可以这样做:

template <typename T>
class MyContainer {
public:
    class HandleType {
    public:
        friend class MyContainer;

        HandleType(const HandleType& rhs) = default;
        HandleType(HandleType&& rhs) = default;
        HandleType& operator =(const HandleType& rhs) = default;
        HandleType& operator =(HandleType&& rhs) = default;
    private:
        // only MyContainer can construct this class
        explicit HandleType(const T& t) : t(t) {}

    private:
        T t;
    };

    HandleType HideType(const T& t) { return HandleType(t); }
    T& UnhideType(HandleType& handle) { return handle.t; }

    void Foo(HandleType&);
};

【讨论】:

  • 当然你可以这样做。问题是,编译器(在我的例子中是 gcc 4.8)会在编译时优化 HandleType 吗?我希望最终的组装就像 HandleType 从未存在过一样。这就是我考虑使用 hacks union+casts 的原因。
  • 我希望没有开销(HideType 中的副本除外,这是您的规范强制要求的)。
猜你喜欢
  • 2014-06-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-05
  • 1970-01-01
  • 2015-03-08
  • 1970-01-01
相关资源
最近更新 更多