防止非类型模板参数中的类型违规

时间:2015-11-21 00:07:52

标签: c++ templates c++14

我通常使用std::size_t,其中模板参数需要积分常数。但我注意到,类型系统并不能保护我免受那些乐意将负数作为参数传递给参数的用户的影响。

例如,以下编译会给出灾难性后果:

#include <iostream>

template<std::size_t I>
struct E1
{
     static void apply()
     {
         std::cout << I << std::endl;
     }
};

template<typename T>
constexpr T a = T { -1 }; 

template<std::size_t... Is>
void E2()
{
    for (auto&& i : {Is...}) std::cout << i << " "; 
    std::cout << std::endl;
}

int main()
{
    E1<-1>::apply();
    E2<-1, -2, -3>(); 
    //std::cout << a<std::size_t>; 
}

Demo

有趣的是,变量模板不允许这样做(取消注释main中的最后一行会导致编译错误)。

structfunction案例是否有任何解决方案/解决方法?

2 个答案:

答案 0 :(得分:7)

这实际上是一个GCC错误,因为这里应该有一个诊断(警告或错误)来缩小。错误报告是Narrowing conversions not caught in non-type template parameters,它有以下示例:

template<        char> void   f () {}
template<unsigned int> void   g () {}


template<unsigned int> struct A {};

int main () {

  f<1024> (); // ill-formed, `char { 1024 }` is a narrowing conversion,
              // see [dcl.init.list]p7

  g<-123> (); // ill-formed, `unsigned int { -123 }` is a narrowing
              // conversion, see [dcl.init.list]p7   

  A<-123>  a; // ill-formed, see previous comment
}

它指出了C ++标准中的相关引用:

  

[temp.arg.nontype] P1;

     
    

非类型非模板模板的模板参数     参数应为以下之一:

         
        
  • 表示积分或枚举的非类型模板参数     type,转换常量表达式(5.19)的类型     模板参数

             

    &lt; snip /&gt;

  •     
  
     

[expr.const] P3;

     
    

类型T的转换常量表达式是一个表达式,     隐式转换为T类型的prvalue,其中转换后的     表达式是核心常量表达式和隐式表达式     转换序列仅包含用户定义的转换,     左值 - 右值转换(4.1),整体促销(4.5)和     积分转换(4.7),而不是缩小转换次数     (8.5.4)。

  
           

[注意:gcc接受testcase.cpp,而clang(正确)发布   相关的诊断。 ]

如上所述,clang正确地为此产生诊断,并出现以下错误:

error: non-type template argument evaluates to -1, which cannot be narrowed to type 'std::size_t' (aka 'unsigned long') [-Wc++11-narrowing]
E1<-1>::apply();
   ^

error: no matching function for call to 'E2'
E2<-1, -2, -3>(); 
^~~~~~~~~~~~~~

作为一种解决方法,您可以使用-Wsign-conversion -Werror both documented here )来警告此案例并将其转为错误( see it live )。我最初认为这会被-Wconversion捕获,但{C}默认情况下会关闭-Wsign-conversion

答案 1 :(得分:3)

我的铿锵拒绝编译:

test.cpp|24 col 8| error: non-type template argument evaluates to -1, which cannot be narrowed to type 'std::size_t' (aka 'unsigned long') [-Wc++11-narrowing]
 E1<-1>::apply();
    ^
test.cpp|25 col 5| error: no matching function for call to 'E2'
 E2<-1, -2, -3>(); 
 ^~~~~~~~~~~~~~
test.cpp|16 col 6| note: candidate template ignored: invalid explicitly-specified argument for template parameter 'Is'
 void E2()

所以,在c ++ 11中,它应该已经被禁止了。

除此之外,它不是问题,因为模板参数实际上不是负面的。也许添加一个静态断言,表明该值在特定范围内。