使用std :: function时,选择自动返回类型的调用操作符而不是构造函数

Hol*_*olt 18 c++ function-object std-function return-type-deduction c++14

以下片段:

#include <functional>

struct X {
    X(std::function<double(double)> fn); // (1)
    X(double, double);                   // (2)

    template <class T>
    auto operator()(T const& t) const {  // (3)
        return t.foo();
    }
};

int main() {
    double a, b;
    auto x = X(a, b);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

... 当使用- 在OSX和godbolt.org上测试时,无法使用clang(4.0.1)和g++(6.3,7.2)-std=c++14进行编译.

但如果符合以下情况,它编译没有问

  • 我删除构造函数(1);
  • 或者我将返回类型设置为具体类型(例如double)(3);
  • 或者如果我使用尾随返回类型(-> decltype(t.foo()))(3);
  • 使用编译-std=c++1z(感谢@bolov).

也许有一些明显我在这里失踪的东西......这段代码有什么问题吗?这是一个错误吗?

Sto*_*ica 21

你是复制初始化x.X由于以下原因,这是一个棘手的类型:

  1. 它可以从任何可以使用double参数调用的可调用对象构造.并且其返回类型可以转换为double.

  2. 它本身是一个可调用的对象,可以使用double参数调用.但是返回类型需要推断.

  3. 有一个编译器生成的复制构造函数X.

我希望,模糊性在这里开始变得明显.需要重载解决方案.

当你删除第一个c'tor时,它以明显的方式消除了歧义.有趣的情况是函数调用操作符.

你看,std::function如果参数的参数之间和返回类型之间的转换是可能的,那么只能通过它传递的可调用来构造(模板化的c'tor只会参与重载决策).这都是在未评估的上下文中完成的,因此模板化的函数调用操作符不会使用,因此不会实例化.

当返回类型是占位符类型时,std::function如果应该构造它,则c'tor不能轻易解决.在重载解析期间编译失败,即使它成功了,X也会选择复制文件.


正如@VTT在评论中所建议的那样,标记接受std::function明确的c'tor也将解决歧义.所有这些都是因为编译器根本不必对隐式转换序列进行排序.

  • 将函数标记为`explicit`的标记构造函数将使其工作. (5认同)
  • @bolov如果你写'X x1(a,b),那就更清楚了.X x2 = x1;`,问题是从`x1`构造`x2`. (4认同)
  • @Holt - 是的.但问题是仍然必须发生重载解析,并且占位符类型干扰检查`std :: function`应该被取消资格. (3认同)
  • @StoryTeller谢谢,我确实理解了问题的根源,但是我不明白它的结果是什么 - 复制构造函数不应该在这里更好地匹配吗?在这种情况下,为什么要实例化`std :: function <double(double)>(X)`的构造函数? (2认同)