2013-03-12 58 views
5

我正在寻找一种方式来配制具有类:转换STL容器<T *>集装箱<T const *>

  • 使用最大“常量性”指针
  • 但其中的STL容器的接口内部变异的尖-to对象
  • 相比于一个非const模拟

理想的情况下没有额外的运行时开销,该解决方案将汇编没有分机ra代码与非const版本的比较,因为const/non-const-ness只是帮助程序员在这里。

这里是我试过到目前为止:

#include <list> 
#include <algorithm> 

using namespace std; 
typedef int T; 

class C 
{ 
public: 
    // Elements pointed to are mutable, list is not, 'this' is not - compiles OK 
    list<T *> const & get_t_list() const { return t_list_; } 

    // Neither elements nor list nor' this' are mutable - doesn't compile 
    list<T const *> const & get_t_list2() const { return t_list_; } 

    // Sanity check: T const * is the problem - doesn't compile 
    list<T const *> & get_t_list3() { return t_list_; } 

    // Elements pointed to are immutable, 'this' and this->t_list_ are 
    // also immutable - Compiles OK, but actually burns some CPU cycles 
    list<T const *> get_t_list4() const { 
     return list<T const *>(t_list_.begin() , t_list_.end()); 
    } 

private: 
    list<T *> t_list_; 
}; 

如果没有解决类型转换,我想就如何配制具有描述的属性的类其他建议。

+0

'T const'不只是简单地转换为'T',你必须使用'const_cast',但这很丑陋,违反了const的要点。 – 2013-03-12 11:27:57

+0

@Tony我想将'T'转换为'T const',这通常是可能的。以下编译:'int x = 2; int const * x_ptr =&x;' – SimonD 2013-03-12 11:39:54

+0

不同的模板专业化是不相关的,它们是有效的不同类型。 – Xeo 2013-03-12 11:42:40

回答

7

让我们假设您暂时可以将list<T*>&转换为list<T const *>&。现在考虑下面的代码:

list<char*> a; 
list<char const*>& b = a; 

b.push_back("foo"); 

a.front()[0] = 'x'; // oops mutating const data 

这是一个与转换T**T const**同样的概念问题。

如果您想要提供对基础数据的只读访问,则需要提供一些自定义视图,可能使用自定义迭代器。

像下面这样。

template <typename It> 
class const_const_iterator { 
private: 
    using underlying_value_type = typename std::iterator_traits<It>::value_type; 

    static_assert(std::is_pointer<underlying_value_type>(), 
        "must be an iterator to a pointer"); 

    using pointerless_value_type = typename std::remove_pointer<underlying_value_type>::type; 

public: 
    const_const_iterator(It it) : it(it) {} 

    using value_type = pointerless_value_type const*; 

    value_type operator*() const { 
     return *it; // *it is a T*, but we return a T const*, 
        // converted implicitly 
        // also note that it is not assignable 
    } 

    // rest of iterator implementation here 
    // boost::iterator_facade may be of help 

private: 
    It it; 
}; 

template <typename Container> 
class const_const_view { 
private: 
    using container_iterator = typename Container::iterator; 

public: 
    using const_iterator = const_const_iterator<container_iterator>; 
    using iterator = const_iterator; 

    const_const_view(Container const& container) : container(&container) {} 

    const_iterator begin() const { return iterator(container->begin()); } 
    const_iterator end() const { return iterator(container->end()); } 

private: 
    Container const* container; 
} 
+0

Wow ...在我的解决方案中有几个新东西:使用[name] = typename T和static_assert ...这需要什么版本的标准? (我已经离开了C++一段时间) – SimonD 2013-03-12 12:06:20

+0

他们来自C++ 11。 '使用A = B;'与'typedef B A'相同;';因为它只是一个风格问题,你可以用typedef替换它。如果条件不符合,'static_assert'会导致编译错误。这对解决方案来说并不是真正必要的,但它有助于提供更好的错误信息。你也可以放弃它。 'std :: remove_pointer'也来自C++ 11,但它可以很容易地在没有C++ 11的情况下实现(https://gist.github.com/rmartinho/5142454)。 – 2013-03-12 12:15:14

+0

理想的解决方案是'编译',生成与常量代码相同的汇编代码。这个解决方案必须使用更多的内存,因为有一个非零大小的新类。 Indirections可能会被优化掉 - 取决于It :: operator *是否可以内联...“const_const_iterator”是否可以从'It'私下派生来去除间接和额外内存使用的级别? – SimonD 2013-03-12 19:42:08

1

不要返回容器。返回迭代器。

+1

@nsgulliver - 如果你唯一的工具是锤子,一切看起来都像钉子。回答**正确的问题通常比盲目地回答被问到的问题更好。 – 2013-03-12 14:41:42

+0

你是绝对正确的!然而,为了激发答案,多提一点细节可能会很棒。 - nsgulliver 4分钟前 – nsgulliver 2013-03-12 14:49:30

+0

返回比迭代器更多的东西的动机是容器可能有其他有用的功能,比如'size()','operator []','front()'等等。样板代码来修补它们。除了写作乏味之外,一个班级的真正目的很快会在琐碎的海洋中变得模糊。 – SimonD 2013-03-12 19:45:19

相关问题