2

已编辑 - 请跳到真正有问题的编辑

我经常在独立函数的字符串帮助程序库中遇到这种情况,我在其中提供函数的重载,其中版本采用 achar和采用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::function 重载,它就会崩溃:

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::function 作为重载时,我收到以下错误消息:

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::string 而不是 std::function?这可能是模棱两可的,因为 std::function 的构造函数是模板化的,并且设计用于获取函数指针等。

因为我的字符串库,具体来说,只使用std::function<bool(char)>and std::function<bool(const std::string&)>,已经 typedef'd,我可以将它们继承到具有显式构造函数的类中。

是否有其他可用的建议或选项?

4

3 回答 3

2

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

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

这些构造函数不应参与重载决议,除非 f 是 Callable

目前 g++4.8 及更高版本实现了这一点。

于 2013-07-11T20:58:11.403 回答
1

您应该能够非常轻松地摆脱这个 SFINAE 自己:

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
于 2013-07-11T22:09:27.603 回答
1

您没有提供独立的复制品,因此很难说出了什么问题。但这里有几个猜测:

  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::function 具有接受任何类型的模板构造函数,包括 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
于 2013-07-11T06:26:51.523 回答