非成员函数中的隐式参数转换模糊性使用std::函数重载

Implicit argument conversion ambiguity in non-member functions overloads with std::function?

本文关键字:函数 模糊性 std 转换 重载 参数 成员      更新时间:2023-10-16

已编辑-请跳到编辑,这是真正的问题

我经常在我的独立函数的字符串帮助程序库中遇到这样的情况,在那里我提供了一个函数的重载,其版本采用char,版本采用std::string

问题是,当传递字符串文字(const char*)时,重载会变得不明确。

示例:

void myFunc(const std::string &subStr);
void myFunc(char character);

这两个函数的实现方式不同,一个针对字符串进行了优化,另一个针对单个字符进行了优化。无论如何,尝试调用myFunc("literal")会导致歧义,尽管我一直希望它调用std::string版本。

这迫使我提供重载的void myFunc(const char *str)版本,它们只是像这样的存根

void myFunc(const char *str)
{
    myFunc(std::string(str));
}

有没有办法让这些存根函数变得不必要?我希望能够使void myFunc(char c)"显式",但不能使非构造函数非成员函数显式。这会立即解决问题=(…

(顺便说一句,为什么你不能明确独立的功能?)

编辑:你知道他们怎么说程序员编码到深夜!(如果你还记得这个笑话,告诉我,因为我最初听到这个笑话时太困了,后来我忘了)

真正的问题

我使用的是MinGW v4.7.2,这个问题与我最初的假设大不相同。

问题是,我有几个过载。是的,这个例子很好用:

void myFunc(const std::string &subStr);
void myFunc(char character);

但是,如果添加std::函数重载,它就会崩溃:

void myFunc(const std::string &subStr);
//Not actually part of the problem; I was confused by part of the error message highlighting this function.
//void myFunc(char character); 
void myFunc(std::function<bool(char)); //<-- The real problem

我的字符串库有std::string、char和std::function重载(偶尔还会有一些重载,用于用大量可选参数简化函数)。

当我将std::函数作为重载时,我会收到以下错误消息:

error: call of overloaded ‘myFunc(const char [15])’ is ambiguous
candidates are:
void myFunc(char) <near match>
no known conversion for argument 1 from ‘const char [15]’ to ‘char’
void myFunc(std::function<bool(char)>)
void myFunc(const string&)

myFunc(char)是我昨晚最初感到困惑的原因。从代码中删除过载,我得到错误消息:

error: call of overloaded ‘myFunc(const char [15])’ is ambiguous
candidates are:
void myFunc(std::function<bool(char)>)
void myFunc(const string&)

这里有一个自包含的可编译示例

如何让字符串文字选择std::字符串而不是std::函数?这可能是不明确的,因为std::function的构造函数是模板化的,并且被设计为接受函数指针等。

特别是,由于我的字符串库只使用std::function<bool(char)>std::function<bool(const std::string&)>,它们已经是typedef'd,所以我可以将它们继承到具有显式构造函数的类中。

是否有其他建议或选择?

你能更新你的编译器吗?您的示例按照g++4.8及更高版本中的预期编译。

这实际上是C++标准中的当前缺陷报告。参见2132。std::函数不明确。它目前处于审查状态,但很可能会被接受。这将确保像您的示例这样的不可调用类型永远不会参与过载解决:

除非f是可调用

目前g++4.8及以上版本实现了这一点。

您没有提供独立的repo,所以很难说出了什么问题。但这里有几个猜测:

  1. 编译器中有一个错误(不太可能)
  2. 您没有正确调用字符重载。你应该这样称呼它:myFunc('c')
  3. 您提供了不正确的调用代码或不正确的方法签名

我想下面的代码片段应该解释应该发生什么以及如何正确地声明和调用方法。注意myOtherFunc捕获文字的技巧。如果没有模板函数,使用字符串周围的智能包装器,可以做得更好,但我将忽略这一点。

您也可以在编译器上试用,看看它是否有效,然后我们就会知道您是否有编译器问题。

实时代码:http://codepad.org/gzB7xWs2

#include <string>
#include <iostream>
using namespace std;
void myFunc(char c) {
    cout << "myFunc called with char" << endl; 
}
void myFunc(const string& s) {
    cout << "myFunc called with string" << endl;
}
void myOtherFunc(char c) {
    cout << "myOtherFunc called with char" << endl; 
}
void myOtherFunc(const string& s) {
    cout << "myOtherFunc called with string" << endl;
}
template <size_t StingSizeWithNullTerminator>
void myOtherFunc(const char (&buf)[StingSizeWithNullTerminator]){
    cout << "myOtherFunc called with literal of size " << (StingSizeWithNullTerminator - 1) << endl;
}
int main() {
    myFunc("string");
    myFunc('c');
    myFunc(string("std string"));
    myOtherFunc("string");
    myOtherFunc('c');
    myOtherFunc(string("string"));
    return 0;
}

输出:

myFunc called with string
myFunc called with char
myFunc called with string
myOtherFunc called with literal of size 6
myOtherFunc called with char
myOtherFunc called with string

更新

现在,通过这个例子,问题就很明显了。问题是没有一个具有确切签名的方法可以接受char[15]。编译器需要执行转换。问题是,它既可以转换为std::string,也可以转换为std::function(因为std:函数有一个模板构造函数,可以接受任何类型,包括char[15])。因此,它无法选择使用哪种转换并放弃。

因此,据我所知,没有干净的解决方案,但这里有一些不那么干净的:

  1. 调用方法时使用对std::string的显式转换
  2. 问问自己(也许告诉我们),myFunc同时接受字符串和函数的原因是什么。也许设计有问题,可以避免使用相同名称的函数
  3. 如果您只需要接受bool(&)(char)函数,则可以使用自定义包装器(请参阅下面的示例)

第三个选项的示例(http://ideone.com/o0NqUf):

#include <iostream>
#include <functional> //Required for std::function.
struct Callback
{
    Callback(bool (&func)(char)): m_func(func)
    {}
    bool operator()(char c) { return m_func(c); }
    bool (&m_func)(char);
};
void myFunc(Callback seperatorFunc)
{
    std::cout << "Callback overload" << std::endl;
}
void myFunc(const std::string &separator)
{
    std::cout << "std::string overload" << std::endl;
}
bool testCallback(char)
{
    return true;
}
int main()
{
    myFunc("String literal");
    myFunc(std::string("std::string"));
    myFunc(testCallback);
    return 0;
}

输出:

std::string overload
std::string overload
Callback overload

您应该能够很容易地将自己排除在之外

template <typename F>
auto myFunc(F f) -> decltype(!f('0'), std::function<bool(char)>(f), void()) {
    std::cout << "std::function<bool(char)> overload" << std::endl;
}

或者,使用C++03编译器(如果您的编译器还没有tr2/type_traits或Boost type traits,则可能使用它们):

template <typename F>
void myFunc(F f, typename std::enable_if<std::is_constructible<
       std::function<bool(char)>, F>::value>::type* = nullptr)
{
    std::cout << "std::function<bool(char)> overload" << std::endl;
}


证明它有效:http://ideone.com/Q87JsV

#include <iostream>
#include <type_traits>
#include <functional>
#if 1
template <typename F>
auto myFunc(F f) -> decltype(!f('0'), std::function<bool(char)>(f), void()) {
    std::cout << "std::function<bool(char)> overload" << std::endl;
}
#else
template <typename F>
void myFunc(F f, typename std::enable_if<std::is_constructible<
       std::function<bool(char)>, F>::value>::type* = nullptr)
{
    std::cout << "std::function<bool(char)> overload" << std::endl;
}
#endif

void myFunc(const std::string &seperator) {
    std::cout << "std::string overload" << std::endl;
}
bool testCallback(char) {
    return true;
}
int main()
{
    myFunc("String literal");
    myFunc(std::string("std::string"));
    myFunc(testCallback);
}

输出:

std::string overload
std::string overload
std::function<bool(char)> overload