非成员函数中的隐式参数转换歧义是否与std :: function重载?

时间:2013-07-11 03:52:08

标签: c++ string char string-literals std-function

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

我经常遇到独立函数的字符串帮助库中的情况,我提供函数的重载,版本采用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)

有没有办法让这些存根函数变得不必要?我希望能够使void myFunc(const std::string &subStr); void myFunc(char character); '显式',但是你不能使非构造函数非成员函数显式化。哪个会立即解决问题。 =(...

(顺便说一句,为什么不能将独立函数显式化?)

修改 你知道他们对程序员编码到深夜的看法! (如果你还记得这个笑话,请告诉我,因为当我最初听到它时我太困了,而且我已经忘了它了)

真正的问题

我正在使用MinGW v4.7.2,问题与我原先假设的帖子有很大的不同。

问题是,我有几个重载。是的,这个例子工作正常:

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 :: 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&)

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

当我将std :: function作为重载时,我收到此错误消息:

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

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

std::function<bool(char)>

Here's a self-contained compilable example.

如何创建一个字符串文字,在std :: function上选择std :: string? 它可能是模棱两可的,因为std :: function的构造函数是模板化的,并且设计用于获取函数指针等。

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

是否有其他建议或选项?

3 个答案:

答案 0 :(得分:2)

你能更新你的编译器吗?您的示例在g++4.8 and above中按预期编译。

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

  

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

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

答案 1 :(得分:1)

你没有提供自包含的复制品,所以很难说出错了。 但这里有几个猜测:

  1. 您在编译器中存在错误(不太可能)
  2. 您没有正确调用字符过载。你应该这样称呼:myFunc('c')。
  3. 您提供了错误的调用代码或不正确的方法签名。
  4. 我想以下代码片段应该解释应该发生什么以及如何正确地声明和调用方法。注意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(&amp;)(char)函数,则可以使用自定义包装器(参见下面的示例)
    4. 第三个选项(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
      

答案 2 :(得分: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