错误 C4996: 'std::_Copy_impl': 函数调用的参数可能不安全

7

我知道这个问题在SO上已经被问了很多次,但这是与其他不同的变体。

编译器错误:调用可能不安全的参数函数

Visual Studio警告C4996

xutility(2227): 警告C4996:'std::_Copy_impl'

失败的代码片段

DWORD dwNumberOfNames = pExportDirectory->NumberOfNames;
LPDWORD dwNames = (LPDWORD)((LPBYTE)hDLL +  pExportDirectory->AddressOfNames);
std::vector< std::string > exports;
std::copy(
    dwNames, 
    dwNames + dwNumberOfNames, 
    [&exports, &hDLL](DWORD  dwFuncOffset)
{
    std::string fname = std::string((PCHAR)((PBYTE)hDLL + dwFuncOffset));
    exports.push_back(fname);
}
);

编译器错误

错误 1 错误 C4996: 'std::_Copy_impl':函数调用的参数可能不安全 - 这个调用依赖于调用者检查传递的值是否正确。要禁用此警告,请使用-D_SCL_SECURE_NO_WARNINGS。有关如何使用Visual C ++“已检查的迭代器”的文档, 请参见C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\xutility2176

问题

考虑到C4996表示该函数已被标记为弃用,问题出在哪里?

  1. 是我使用了 std::copy,而微软认为这是不安全的并将其弃用吗?
  2. 是因为我使用了 C 数组std::copy 吗?
  3. 是因为我使用 Lambda 表达式的方式吗?
  4. 如果 std::copy 已被弃用,那么在需要可移植性的情况下,有什么替代方法呢?

注意

我知道如何抑制警告,但我很想知道问题的根本原因。

对我来说同样重要的是,知道如何以不影响代码质量的方式处理此问题的可移植方法。


1
std::copy的第三个参数应该是一个输出迭代器,但你有一个不返回迭代器的lambda函数。 - Adrian McCarthy
使用std::copy时,您不能确定输出迭代器是否有足够的“空间”,因此可能会发生缓冲区溢出。 - Jarod42
@Jarod42 请见下面我的回答,如果您在标准容器中使用 std::back_inserter,则不会出现溢出问题。 - Kevin Anderson
2个回答

6

您没有按照 MSDN 的要求调用 std::copy 函数:http://msdn.microsoft.com/en-us/library/x9f6s1wf.aspx

该函数的签名为:

template<class InputIterator, class OutputIterator> 
   OutputIterator copy( 
      InputIterator _First,  
      InputIterator _Last,  
      OutputIterator _DestBeg 
   );

那里没有地方放置一个函数对象/lambda表达式。

你也没有调用std::copy_ifhttp://msdn.microsoft.com/en-us/library/ee384415.aspx

template<class InputIterator, class OutputIterator, class BinaryPredicate>
   OutputIterator copy_if(
      InputIterator _First, 
      InputIterator _Last,
      OutputIterator _Dest,
      Predicate _Pred
    );

由于您没有输出迭代器,也没有谓词返回布尔值。
看起来您需要使用 std::transformhttp://msdn.microsoft.com/en-us/library/391xya49.aspx
template<class InputIterator, class OutputIterator, class UnaryFunction> 
   OutputIterator transform( 
      InputIterator _First1,  
      InputIterator _Last1,  
      OutputIterator _Result, 
      UnaryFunction _Func 
   ); 

你应该在输出迭代器中返回想要的值。所以代码会像这样:

std::transform(
    dwNames, 
    dwNames + dwNumberOfNames,
    std::back_inserter(exports),
    [&hDLL](DWORD  dwFuncOffset) // This lambda is WRONG
{
    // THIS LINE IS WRONG 
    std::string fname = std::string((PCHAR)((PBYTE)hDLL + dwFuncOffset));
    return fname;
}
);

我的lambda表达式有误。您需要将数组的ELEMENTS(我不确定类型)作为lambda的参数输入,并返回您想要插入到“exports”向量中的内容。
您可能还不知道“back_inserter”。它在“”头文件中。请参见这里:http://msdn.microsoft.com/en-us/library/12awccbs.aspx和这里:http://www.cplusplus.com/reference/iterator/back_inserter/ 这可能不是100%的答案,但是有了这些,我认为您可以到达您想要去的地方。

他似乎几乎在使用 std::for_each - Mooing Duck

1

我很感谢你的回答。你对你的猜测有什么理由吗?编译器错误指的是std::_Copy_impl,而不是strcpy。你在哪里看到调用strcpy的地方? - Abhijit
问题中表述的信息强烈暗示警告正是指 std::copy 本身,并且该问题并未使用 char *(无论何处都没有警告)。 - user743382
我以前在使用strcpy、strlen等函数时也遇到过类似的警告。我刚才检查了一下,这个警告是因为你没有使用一个经过验证的迭代器。 - CS Pei
@JohnSmith:checked iterator是C++标准的一部分还是仅限于微软?这会使我的代码可移植吗?使用lambda表达式与stdext::checked_array_iterator的正确方法是什么? - Abhijit
我认为checked iterator是微软特有的。我相信stdext也是来自于微软。我不知道lambda部分的答案。 - CS Pei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接