【问题标题】:Instantiate a class template with an invalid method使用无效方法实例化类模板
【发布时间】:2014-10-13 08:32:44
【问题描述】:

我正在编写一个类IteratorIterable,它包装了一个容器类(一个带有begin()end() 方法返回一些迭代器的类),以便可以迭代被包装类的迭代器。这个想法基于this post。我的代码如下所示(为简洁起见,省略了一些方法):

template <class T>
class IteratorIterable
{
private:
    T& container;

public:
    typedef decltype(container.begin()) BackendIterator;

public:
    class IteratorIterator
    {
    public:
        IteratorIterator() : it() {}
        IteratorIterator(BackendIterator it) : it(it) {}
        IteratorIterator(const IteratorIterator& other) : it(other.it) {}

        IteratorIterator& operator=(const IteratorIterator& other) { if (&other == this) return *this; it = other.it; return *this; }

        BackendIterator operator*() const { return it; }
        const BackendIterator* operator->() const { return &it; }

        bool operator==(const IteratorIterator& other) { return it == other.it; }
        bool operator !=(const IteratorIterator& other) { return it != other.it; }

        IteratorIterator operator+(size_t n) { return IteratorIterator(it + n); }

        IteratorIterator& operator++() { ++it; return *this; }
        IteratorIterator operator++(int) { IteratorIterator cpy(*this); ++(*this); return cpy; }

    private:
        BackendIterator it;
    };

public:
    IteratorIterable(T& container) : container(container) {}

    IteratorIterator begin() const { return IteratorIterator(container.begin()); }
    IteratorIterator end() const { return IteratorIterator(container.end()); }
};


template <class T>
IteratorIterable<T> ItIt(T& container)
{
    return IteratorIterable<T>(container);
}

这里的问题是,IteratorIterator 中的operator+() 方法只对随机访问BackendIterator 有效,否则后端没有定义加法运算符。我希望我的 IteratorIterator 仅在后端支持时才提供此方法。

考虑这个示例代码:

typedef list<int> Cont;

Cont vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};

IteratorIterable<Cont> itb(vec);

IteratorIterable<Cont>::IteratorIterator beg = itb.begin();
IteratorIterable<Cont>::IteratorIterator it = beg;

it++;
//it = beg+1;

printf("%d\n", **it);

使用it++ 行时编译得很好,但是 - 正如预期的那样 - 使用it = beg+1 行失败,因为list&lt;int&gt;::iterator 不是随机访问。我想这是因为如果我实际上不实例化 IteratorIterator::operator+(),编译器不会关心。

我知道模板只允许对某些模板参数有效,但这里 class 是模板化的,而不是 method。当该方法从未用于此特定实例化时,实例化其中一个方法无效的类模板是否正确? GCC 和 Clang 没有抱怨,但按照 C++ 标准是否正确?

【问题讨论】:

    标签: c++ templates c++11


    【解决方案1】:

    是的,标准保证类模板的隐式实例化只会导致声明的隐式实例化,而不是成员函数的定义。

    §14.7.1 [temp.inst]

    1  ... 类模板特化的隐式实例化导致隐式 声明的实例化,但不是定义的实例化、默认参数或类成员函数、成员类、范围成员枚举、静态数据成员和成员模板的异常规范;它会导致无范围成员枚举和成员匿名联合的定义的隐式实例化。

    11   实现不应隐式实例化函数模板、变量模板、成员模板、非虚拟成员函数不需要实例化的类模板的成员类或静态数据成员...

    请注意,如果您显式实例化该类,则无论您是否使用operator+,您的代码都将无法编译

    template class IteratorIterable<std::list<int>>;
    

    除非BackendIterator 是随机访问迭代器,否则您可以通过使用enable_if 来SFINAE 重载集中的成员函数来防止这种情况发生。

    template<typename Iter = BackendIterator>
    typename std::enable_if<
        std::is_same<typename std::iterator_traits<Iter>::iterator_category,
                     std::random_access_iterator_tag>::value,
        IteratorIterator>::type
    operator+(size_t n) 
    {
        return IteratorIterator(it + n); 
    }
    

    Live demo

    【讨论】:

    • @T.C.谢谢,那比我引用的还要好
    • 不确定 static_assert 的参数包技巧 - 14.6/p8 “如果可变参数模板的每个有效特化都需要一个空模板参数包,则模板格式错误,不需要诊断。 "
    • @T.C.嗯,蒂尔。我已经看到了一些其他的答案,人们用它来防止不必要的专业化。我已经摆脱它了,谢谢指出。
    【解决方案2】:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-01
      • 2020-12-27
      • 1970-01-01
      • 2020-09-11
      • 2019-09-06
      相关资源
      最近更新 更多