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

xml*_*lmx 0 c++ templates compiler-errors c++11

#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);
}
Run Code Online (Sandbox Code Playgroud)

VS 2015年产量:

错误C2678:二进制'<<':找不到运算符,它采用类型'std :: vector>'的左手操作数(或者没有可接受的转换)

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

ild*_*arn 6

你的函数接受一个右值引用,但你传递一个左- Left<T, Alloc>&&不是转发引用,所以用它当作这样std::forward等是不正确的.现在我们将禁止收集rvalues来简化事情:

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;
}
Run Code Online (Sandbox Code Playgroud)

以上是更接近一步,但如果传递左值,将无法工作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;
}
Run Code Online (Sandbox Code Playgroud)

Online Demo

这有效,但不会给我们任何简单的方法来支持集合rvalues.这里正确的解决方案是IMO要停止使用模板模板,要么static_assert是容器的value_type匹配,要么是TSFINAE,如果不是:

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;
}
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

Online Demo

铌上面的实现只允许使用与运营商的精确匹配Coll::value_type,但它可能是明智的,允许任何可以被转换成 Coll::value_type -实现这一点,只需更换std::is_samestd::is_convertible.