【问题标题】:C++ Ambiguous call to overloaded functionC++ 对重载函数的模糊调用
【发布时间】:2011-12-12 20:54:41
【问题描述】:

我有以下代码用于“安全”strncpy()——基本上,它的包装器自动为字符串缓冲区采用固定的数组大小,因此您不必做额外的工作来传递它们(而且这种方便更安全因为您不会意外地为固定数组缓冲区输入错误的大小)。

inline void MySafeStrncpy(char *strDest,size_t maxsize,const char *strSource)
{
    if(maxsize)
    {
        maxsize--;
        strncpy(strDest,strSource,maxsize);
        strDest[maxsize]=0;
    }
}

inline void MySafeStrncpy(char *strDest,size_t maxDestSize,
    const char *strSource, size_t maxSourceSize)
{
    size_t minSize=(maxDestSize<maxSourceSize) ? maxDestSize:maxSourceSize;
    MySafeStrncpy(strDest,minSize,strSource);
}

template <size_t size>
void MySafeStrncpy(char (&strDest)[size],const char *strSource)
{
    MySafeStrncpy(strDest,size,strSource);
}

template <size_t sizeDest,size_t sizeSource>
void MySafeStrncpy(char (&strDest)[sizeDest],
    const char (&strSource)[sizeSource])
{
    MySafeStrncpy(strDest,sizeDest,strSource,sizeSource);
}

template <size_t sizeSource>
void MySafeStrncpy(char *strDest,size_t maxDestSize,
    const char (&strSource)[sizeSource])
{
    MySafeStrncpy(strDest,maxDestSize,strSource,sizeSource);
}

使用代码在Visual C++ 2008编译时出现错误:

char threadname[16];
MySafeStrncpy(threadname,"MainThread");

error C2668: 'MySafeStrncpy' : ambiguous call to overloaded function
>        could be 'void MySafeStrncpy<16,11>(char (&)[16],const char (&)[11])'
>        or       'void MySafeStrncpy<16>(char (&)[16],const char *)'
>        while trying to match the argument list '(char [16], const char [11])'

我在这里做错了什么?

在确定调用哪个模板函数时,编译器似乎无法确定字符串文字 "MainThread" 是否应该被视为 const char *const char[11]

我希望它将字符串文字视为const char[11] 并选择void MySafeStrncpy&lt;16,11&gt;(char (&amp;)[16],const char (&amp;)[11]) 变体,因为这是“最安全的”。

还有两个对答案的限制:1)我无法切换编译器(代码在其他编译器上编译)和 2)公司不允许我使用外部模板库作为解决方案。

【问题讨论】:

  • 你会很高兴听到 g++ 编译它:)
  • @themel:感谢您的关注。我在我的问题中澄清了这是 Visual C++ 2008 中的一个问题。
  • 仅供参考,当我发现重载(大约在 1997 年)时,我经常使用它并认为它是多么酷/多么聪明。大约 5 年前,我几乎完全停止使用重载 - 是的,我现在有了 print1、print2 等函数。主要原因:当我使用 IDE 搜索我的项目时,我只得到我要查找的内容,无需从长长的名单。
  • @radim:这些功能都彼此相邻,一点也不令人困惑。另外,这段代码显然在某些编译器上编译得很好:-(

标签: c++ arrays templates visual-c++ overloading


【解决方案1】:

根据 13.3.3.1.1,数组到指针的转换精确匹配 rank,所以这个函数调用在标准规范中可能是模棱两可的。 如果允许您更改定义:

template <size_t size>
void MySafeStrncpy(char (&strDest)[size],const char *strSource)

到:

template <size_t size, class T>
void MySafeStrncpy(char (&strDest)[size], T strSource)

喜欢here, 那么这可能是最简单的解决方法。

【讨论】:

  • "数组到指针的转换具有完全匹配等级" 它仍然是转换,而不是完全匹配。什么时候没有转化不比转化好?
  • @curiousguy:可能...但是,无需转换也具有相同的完全匹配排名。此外,当我使用简化代码进行测试时,VC8、gcc4.3.4/4.5.1(ideone) Comeau online 和 LLVM online 报告了歧义。 C++中的重载解析规则对我这可怜的大脑来说太复杂了,所以我希望语言律师解释一下。
  • 我非常惊讶。 [over.ics.rank]/3/1/2 "标准转换序列 S1 是比标准转换序列 S2 更好的转换序列,如果" "S1 是S2 的适当子序列" "身份转换序列被认为是任何非身份转换序列的子序列"。等级 Exact Match 并不意味着类型完全匹配,这只是 std C++ 的基本知识。我相信 [over.ics.rank] 自第一个 C++ 标准以来没有改变(显然添加了 std::initializer_list 内容)。
  • @curiousguy:谢谢。显然我缺乏基础知识:)
【解决方案2】:

char-array/const-char-array 和 char-array/const-char-pointer 的重载无法通过重载解析逻辑相互区分(至少 Microsoft 逻辑 - 此代码在GCC。不知道标准在这里说的。)。您需要以某种方式区分它们,例如通过重命名数组的函数或添加一个虚拟参数。

一个优雅的解决方案是使用 boost::enable_if 和 boost::is_array。

【讨论】:

  • 让我们说 boost 不是一个选项——如果我在项目中添加 boost,它不会在一百万年后与团队一起飞行。我怎样才能在没有提升的情况下优雅地做到这一点?我只是想让它把一个字符串常量当作一个字符数组而不是一个字符指针。
  • 如果你能让 gcc 飞起来,切换到 gcc,但不知何故我怀疑:-)。否则,您必须添加某种指示,表明您正在使用数组; MySafeStrncpyPA、MySafeStrncpyAA 和 MySafeStrncpyAP 似乎是最简单的,我想不出更好的解决方案。
  • 抱歉也不能切换到 GCC。让我们只说 Visual C++ 并且没有外部模板库是答案的附加约束。
【解决方案3】:

当你使用你的功能时:

MySafeStrncpy(threadname,"MainThread");

你没有在线程名和“MainThread”之间传递 size_t 参数,但你已经在函数定义中定义了。

应该是这样的:

MySafeStrncpy(threadname, sizeof threadname, "MainThread");

如果您不想传递参数,请在您的函数定义中将其设为默认值。

【讨论】:

  • 我想让它打电话给template &lt;size_t sizeDest,size_t sizeSource&gt; void MySafeStrncpy(char (&amp;strDest)[sizeDest], const char (&amp;strSource)[sizeSource])
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-11
  • 2014-09-05
  • 2016-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多