使用 std::function 时选择自动 return 类型的调用运算符而不是构造函数
Call operator with auto return type being chosen instead of constructor when using std::function
以下片段:
#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;
}
...使用 -std=c++14
时无法同时使用 clang
(4.0.1) 和 g++
(6.3, 7.2) 进行编译 — 在 OSX 上测试并且godbolt.org.
但是编译没有问题如果:
- 我删除构造函数
(1)
;
- 或者我在
(3)
; 中将 return 类型设置为具体类型(例如 double
)
- 或者如果我在
(3)
; 中使用尾随 return 类型 (-> decltype(t.foo())
)
- 使用
-std=c++1z
编译(感谢@bolov)。
也许我在这里遗漏了一些明显的东西...这段代码有什么问题吗?这是一个错误吗?
您正在复制初始化 x
。 X
是一个棘手的类型,原因如下:
它可以从任何可以用双参数调用的可调用对象构造。并且其 return 类型可转换为 double。
它本身就是一个可调用对象,可以用双参数调用。但是return类型需要推导。
编译器为 X
生成了复制构造函数。
我希望歧义在这里开始变得明显。需要重载解析。
当您删除第一个 c'tor 时,它以明显的方式消除了歧义。有趣的案例是函数调用运算符。
你看,std::function
只能从传递给它的可调用对象构造(模板化的 c'tor 将只参与重载决议)如果参数到参数之间的转换,以及 return 类型是可能的。这一切都是在未评估的上下文中完成的,因此模板化函数调用运算符未被 ODR 使用,因此未被实例化。
当return 类型是占位符类型时,std::function
c'tor 无法轻易判断是否应该构造。在重载解析期间编译失败,即使它成功了,也会选择 X
的复制 c'tor。
正如@VTT 在评论中建议的那样,将接受 std::function
的 c'tor 标记为显式也将解决歧义。这一切都是因为编译器根本不必对隐式转换序列进行排序。
以下片段:
#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;
}
...使用 -std=c++14
时无法同时使用 clang
(4.0.1) 和 g++
(6.3, 7.2) 进行编译 — 在 OSX 上测试并且godbolt.org.
但是编译没有问题如果:
- 我删除构造函数
(1)
; - 或者我在
(3)
; 中将 return 类型设置为具体类型(例如 - 或者如果我在
(3)
; 中使用尾随 return 类型 ( - 使用
-std=c++1z
编译(感谢@bolov)。
double
)
-> decltype(t.foo())
)
也许我在这里遗漏了一些明显的东西...这段代码有什么问题吗?这是一个错误吗?
您正在复制初始化 x
。 X
是一个棘手的类型,原因如下:
它可以从任何可以用双参数调用的可调用对象构造。并且其 return 类型可转换为 double。
它本身就是一个可调用对象,可以用双参数调用。但是return类型需要推导。
编译器为
X
生成了复制构造函数。
我希望歧义在这里开始变得明显。需要重载解析。
当您删除第一个 c'tor 时,它以明显的方式消除了歧义。有趣的案例是函数调用运算符。
你看,std::function
只能从传递给它的可调用对象构造(模板化的 c'tor 将只参与重载决议)如果参数到参数之间的转换,以及 return 类型是可能的。这一切都是在未评估的上下文中完成的,因此模板化函数调用运算符未被 ODR 使用,因此未被实例化。
当return 类型是占位符类型时,std::function
c'tor 无法轻易判断是否应该构造。在重载解析期间编译失败,即使它成功了,也会选择 X
的复制 c'tor。
正如@VTT 在评论中建议的那样,将接受 std::function
的 c'tor 标记为显式也将解决歧义。这一切都是因为编译器根本不必对隐式转换序列进行排序。