以下代码段:
#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;
}
...使用clang
时无法使用g++
(4.0.1)和-std=c++14
(6.3,7.2)进行编译 - 在OSX和godbolt.org上进行了测试。
但如果符合以下条件,则编译没有问题:
(1)
; double
中将返回类型设置为具体类型(例如(3)
); -> decltype(t.foo())
中使用尾随返回类型((3)
); -std=c++1z
编译(感谢@bolov)。也许有一些明显我在这里缺失的东西......这段代码有什么问题吗?这是一个错误吗?
答案 0 :(得分:21)
您正在初始化x
。由于以下原因,X
是一种棘手的类型:
它可以从任何可以使用double参数调用的可调用对象构造。并且其返回类型可以转换为double。
它本身是一个可调用的对象,可以使用double参数调用。但是返回类型需要推断。
编译器为X
生成了复制构造函数。
我希望这里的模糊性开始变得明显。需要重载解决。
当您删除第一个c时,它会以明显的方式摆脱歧义。有趣的情况是函数调用操作符。
你知道,std::function
只能通过它传递的可调用构造(模板化的c只会参与重载决策)如果参数的参数之间和返回类型之间的转换是可能的。这都是在未评估的上下文中完成的,因此模板化的函数调用操作符不会被使用,因此不会被实例化。
如果返回类型是占位符类型,则std::function
c&tor;如果应该构造它,则无法轻松解析。在重载解析期间编译失败,即使成功完成,也会选择X
的副本c。
正如@VTT在评论中所建议的那样,标记接受std::function
显式的c也将解决歧义。所有这些都是因为编译器根本不必对隐式转换序列进行排名。