为什么模板模板参数不能按预期工作?

时间:2016-07-16 04:57:14

标签: c++ templates c++11 compiler-errors

#include <vector>

template
<
    typename T,
    typename Alloc,
    template<typename, typename> class Left
>
Left<T, Alloc>&&
operator <<(Left<T, Alloc>&& coll, T&& value)
{
    coll.push_back(std::forward<T>(value));
    return std::forward<Left<T, Alloc>>(coll);
}

using namespace std;

int main()
{
    vector<int> c1;
    c1 << int(8);
}

VS 2015输出:

错误C2678:二进制'&lt;&lt;' :没有找到哪个运算符采用'std :: vector&gt;'类型的左手操作数(或者没有可接受的转换)

为什么模板模板参数不能按预期工作?

1 个答案:

答案 0 :(得分:6)

您的函数采用右值引用,但您传递左值 - Left<T, Alloc>&& 转发引用,因此请使用std::forward等对其进行处理。是不正确的。现在我们不允许收集价值来简化事情:

template<
    typename T,
    typename Alloc,
    template<typename, typename> class Left
>
Left<T, Alloc>& operator <<(Left<T, Alloc>& coll, T&& value) {
    coll.push_back(std::forward<T>(value));
    return coll;
}

上述内容更接近一步,但will not work如果为value传递了左值。一种选择是强制Left的正确参数:

template<
    typename T,
    typename Alloc,
    template<typename, typename> class Left
>
Left<typename std::decay<T>::type, Alloc>&
operator <<(Left<typename std::decay<T>::type, Alloc>& coll, T&& value) {
    coll.push_back(std::forward<T>(value));
    return coll;
}

Online Demo

这有效,但不会给我们任何简单的方法来支持收集价值。这里正确的解决方案IMO将停止使用模板模板,static_assert表示容器value_type匹配T或SFINAE运营商离开时如果不是:

template<typename Coll, typename T>
Coll& operator <<(Coll& coll, T&& value) {
    static_assert(std::is_same<
            typename std::decay<T>::type,
            typename Coll::value_type
        >::value,
        "T does not match Coll::value_type"
    );
    coll.push_back(std::forward<T>(value));
    return coll;
}

Online Demo

template<typename Coll, typename T>
typename std::enable_if<std::is_same<
        typename std::decay<T>::type,
        typename Coll::value_type
    >::value,
    Coll&
>::type
operator <<(Coll& coll, T&& value) {
    coll.push_back(std::forward<T>(value));
    return coll;
}

Online Demo

完成这项工作后,现在如果您决定要支持集合右值,那么这很简单;以static_assert实现为例:

template<typename Coll, typename T>
Coll&& operator <<(Coll&& coll, T&& value) {
    static_assert(std::is_same<
            typename std::decay<T>::type,
            typename std::decay<Coll>::type::value_type
        >::value,
        "T does not match Coll::value_type"
    );
    coll.push_back(std::forward<T>(value));
    return std::forward<Coll>(coll);
}

Online Demo

N.b。上述实现仅允许使用完全匹配Coll::value_type的运算符,但允许任何可以转换为 Coll::value_type的任何内容都是明智的。 } - 要实现这一点,只需将std::is_same替换为std::is_convertible