【问题标题】:Partial template specialization with non-type parameters: GCC vs MSVS非类型参数的部分模板特化:GCC vs MSVS
【发布时间】:2017-02-09 17:10:10
【问题描述】:

考虑这个简单的模板特化:

template<typename T, size_t I>
struct S {};

template<typename T>
struct S<T, std::tuple_size<T>::value> {};

GCC 不编译它,因为它在模板参数std::tuple_size&lt;T&gt;::value 中使用模板参数T

错误:模板参数 'std::tuple_size<_tp>::value' 涉及模板参数

现在让我们在tuple_size 模板参数中将T 替换为typename std::remove_reference&lt;T&gt;::type

// Using primary structure template from previous example.
template<typename T>
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {};

这段代码仍然在模板参数中使用模板参数,但是 GCC 编译它没有任何错误或警告。为什么?

现在,如果我们尝试使用带有 /std:c++latest 标志的 MSVS 编译第二个示例,它会因错误 C2755 而停止:

偏特化的非类型参数必须是简单的 标识符

这个奇怪的限制是什么?当I 等于元组大小时,我想停止编译时递归。​​

那么他们谁错了:MSVS 还是 GCC?

请注意,即使没有任何模板实例化,MSVS 也会报告错误,而 GCC 可以很好地处理所有这些实例:

S<std::tuple<int, float>, 9> s1;
S<std::tuple<int, float>, 2> s2;
S<int, 42> s3;

我使用 MSVS Community 2015 Update 3 及其默认编译器和 GCC 6.2.1。

试过 Clang 3.8.0。它不会编译两个 sn-ps,并出现类似于 GCC 消息的错误:

错误:非类型模板参数依赖于模板参数 偏特化

【问题讨论】:

    标签: c++ gcc visual-c++ language-lawyer template-specialization


    【解决方案1】:

    在过去几年中,标准中处理部分类模板专业化可行性的特定部分已多次更改。 [temp.class.spec.match] 中的原始限制为:

    部分特化的非类型实参表达式不应涉及部分特化的模板形参,除非实参表达式是简单的标识符

    您的代码显然与此冲突,std::tuple_size&lt;T&gt;::value 不是标识符。

    然后在cwg issue 1315之后改为:

    每个 template-parameter 应在非推断上下文之外的 template-id 中至少出现一次。

    但我们没关系 - T 在非推导上下文中用作第一个模板参数。在template auto 之后,它现在显示为:

    如果部分特化的模板参数由于其结构而无法推导出 template-parameter-listtemplate-id,程序格式不正确。

    但我们在那里也很好。可以推断,您具有正确的“结构”-您的专业化是在与主要位置相同的位置使用非类型模板参数,并且它们应该匹配得很好。


    我认为按照 1315 的分辨率(我认为是 post-C++14),代码应该格式正确,但 gcc 和 clang 都拒绝它。一个不幸的解决方法是使用两个 type 参数:

    template <class T, class I>
    struct S;
    
    template<typename T>
    struct S<T, typename std::tuple_size<T>::type> {};
    
    template <size_t I>
    using size_ = std::integral_constant<size_t, I>;
    
    int main() {
        S<std::tuple<int>, size_<1>> s;
    }
    

    gcc 和 clang 都接受那个。

    【讨论】:

    • 一个非常简单的clang修复方法是添加一个显式转换(std::tuple_size也支持sizeof()):template&lt;typename T&gt; struct S&lt;T, (size_t) std::tuple_size&lt;T&gt;::value&gt; {}我不明白为什么gcc不接受T就其本身而言,但对T::something 很好:template&lt;typename T&gt; struct S&lt;T, std::tuple_size&lt;typename T::something&gt;::value&gt; {};template&lt;typename T&gt; struct S&lt;T, std::tuple_size&lt;T&gt;::value&gt; {};
    猜你喜欢
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多