【问题标题】:Why does template template parameters not work as expected?为什么模板模板参数不能按预期工作?
【发布时间】:2016-11-19 08:38:08
【问题描述】:
#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 : 二进制 '' 类型的左侧操作数的运算符(或没有可接受的转换)

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

【问题讨论】:

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


    【解决方案1】:

    你的函数接受一个右值引用,但你传递的是一个左值——Left&lt;T, Alloc&gt;&amp;&amp; 不是一个转发引用,所以用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

    注意上面的实现只允许使用 exact matchesColl::value_type 的运算符,但允许任何可以转换为 Coll::value_type 的东西可能是明智的——实现这个,只需将std::is_same 替换为std::is_convertible

    【讨论】:

    • 为什么 Left<...>&& 不是转发引用? (为什么不认为是类型推断的情况?)
    • @kfsone :该标准将转发引用定义为“对 cv-unqualified 模板参数的右值引用”;专门的模板模板参数与模板参数不同。 :-]
    • 感谢您的解释。我可以看到这是一个常见的误解来源
    猜你喜欢
    • 2021-12-27
    • 2019-11-16
    • 2016-11-19
    • 1970-01-01
    • 2019-06-29
    • 2011-10-01
    • 2011-08-28
    • 1970-01-01
    相关资源
    最近更新 更多