仅当模板参数中存在typedef时才创建typedef

时间:2017-08-30 16:48:09

标签: c++ c++11 templates sfinae

我正在尝试创建通用容器包装器。

template<typename type>
class ContainerWrapper
{
public:
  using allocator_type = typename type::allocator_type;
  using size_type = typename type::size_type;
  using difference_type = typename type::difference_type;
  using pointer = typename type::pointer;
  using const_pointer = typename type::const_pointer;
  using reference = typename type::reference;
  using const_reference = typename type::const_reference;
  using iterator = typename type::iterator;
  using const_iterator = typename type::const_iterator;
  using reverse_iterator = typename type::reverse_iterator;
  using const_reverse_iterator = typename type::const_reverse_iterator;
  using value_type = typename type::value_type;

  iterator begin() noexcept { return container.begin(); }
  const_iterator begin() const noexcept { return container.begin(); }
  iterator end() noexcept { return container.end(); }
  const_iterator end() const noexcept { return container.end(); }
  reverse_iterator rbegin() noexcept { return container.rbegin(); }
  const_reverse_iterator rbegin() const noexcept { return container.rbegin(); }
  reverse_iterator rend() noexcept { return container.rend(); }
  const_reverse_iterator rend() const noexcept { return container.rend(); }
  const_iterator cbegin() const noexcept { return container.cbegin(); }
  const_iterator cend() const noexcept { return container.cend(); }
  const_reverse_iterator crbegin() const noexcept { return container.crbegin(); }
  const_reverse_iterator crend() const noexcept { return container.crend(); }
protected:
  ContainerWrapper() {}
  type& getContainer() { return container; }
  const type& getContainer() const { return container; }
private:
  type container;
};

但并非所有容器都有各种迭代器。是否可以仅在容器类型存在时公开它们?像

这样的东西
using const_reverse_iterator = typename std::enable_if_t<std::is_class<typename type::const_reverse_iterator>::value, typename type::const_reverse_iterator>::type;

我只能使用C ++ 11(gcc 4.7)。当然,我可以为不同的容器创建不同的包装器,但我更喜欢使用一个通用包装器。

3 个答案:

答案 0 :(得分:3)

您可以为每种类型创建一个基类,并使用SFINAE专门化该基础:

template <typename...> struct void_type
{
    using type = void;
};

template <typename T, typename = void> struct ContainerWrapper_Base_const_reverse_iterator{};
template <typename T> struct ContainerWrapper_Base_const_reverse_iterator
    <T, typename void_type<typename T::const_reverse_iterator>::type>
{
    using const_reverse_iterator = typename T::const_reverse_iterator;
};

// ... Similar bases for each conditional alias.

template <typename type> class ContainerWrapper
: public ContainerWrapper_Base_const_reverse_iterator<type>
    // ... Inherit from all those bases
{
    // ...
};

如果您确定它们总是一起定义或根本没有定义,您可以将类似的类型放入单个基础中。

此外,您可能希望使用预处理器生成这些基础,可能还有x-macros。

另请注意,上述代码可以使用C ++ 17 std::void_t或手写template <typename...> using void_t = void;进行简化,但在GCC 4.7中无法正常工作。

答案 1 :(得分:3)

原始阵列容器是否足够你? 无论如何:

使用SFINAE,重载分辨率和继承。

namespace detail {
    using std::rbegin;
    template <class T>
    auto reverse_iter_typedefs(long) {
        struct {} r;
        return r;
    }
    template <class T, class X = decltype(rbegin(std::declval<T&>()))>
    auto reverse_iter_typedefs(int) {
        struct {
            using reverse_iterator = decltype(rbegin(std::declval<T&>()));
        } r;
        return r;
    }
}

template <class T>
ContainerWrapper : decltype(detail::reverse_iter_typedefs<T>(1)), ...

答案 2 :(得分:1)

您有没有理由不使用auto功能?

使用HolyBlackCat的代码,您应该考虑将明确(或应该)组合在一起的类型和函数分组,并使用auto来避免模糊函数调用:

template <typename...> struct void_type { using type = void; };
template<typename type, typename = void>
struct conditional_derive_iterator {
protected:
    conditional_derive_iterator(type*const container) {}
};
template<typename type>
struct conditional_derive_iterator
    <type, typename void_type<typename type::iterator>::type>
{
    // grouping types
    using iterator = typename type::iterator;
    using const_iterator = typename type::const_iterator;
    // this should work with gcc. avoids ambiguity. cleaner code
    inline constexpr auto begin() const noexcept { return c->begin(); }
    inline constexpr auto end() const noexcept { return c->end(); }
protected:
    // this will let you get access to the container
    conditional_derive_iterator(type*const container) : c(container) {}
private:
    type*const c;
};

template<typename type>
class ContainerWrapper :
    public conditional_derive_iterator<type>
{
public:
    // for demo purpose. however, make sure to pass &container to your bases
    ContainerWrapper(const type& c) :
        container(c),
        conditional_derive_iterator(&container) {}
    // continue code...
};

class UselessType{};

void main()
{
    ContainerWrapper<UselessType> a(UselessType{});
    ContainerWrapper<std::vector<int>> b({1,2,3});
    auto itera = a.begin(); // this will not compile
    auto iterb = b.begin(); // this will compile
}

虽然auto func()...是c ++ 14及以上,但我相信auto func() -> return_type是c ++ 11,所以begin / end看起来像这样:

inline constexpr auto begin() const noexcept -> decltype(iterator{}, const_iterator{}) { return c->begin(); }

以下是一个示例:https://repl.it/K9g5/0

您还应该查看此会员检测器:https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Member_Detector

它可以派上用场。