为什么g ++在带有转换运算符和不可访问函数调用运算符的类型中失败了std :: function <> init?

rol*_*ear 7 c++ gcc language-lawyer c++11

这段代码在g ++ 4.9及更高版本(包括从svn current构建时)失败,但是使用clang ++和microsofts编译器(来自VS2015)编译时没有警告.

#include <functional>

struct A {
  void operator()() const {}
};
struct B {
  void operator()() const {}
};
struct C : private A, private B
{
  operator std::function<void()>() const { return nullptr; }
};

int main()
{
  std::function<void()> f{C{}};
}
Run Code Online (Sandbox Code Playgroud)

建设f中的main()失败,因为operator()是在结构暧昧C.

为什么g ++认为这是不明确的?函数调用操作符C是私有继承的,不可访问.向void operator()() conststruct 添加私有或显式删除C会使代码编译并按预期使用转换运算符.为什么这些无法访问的运营商在无法访问的继承运营商时不会出现问题?

Okt*_*ist 3

构造函数:template<class F> function(F f);

C++11:

f应用于Callable参数类型ArgTypes和返回类型R

C++14:

不应参与重载决策,除非fCallable针对参数类型ArgTypes...和返回类型R


std::function在 C++11 中,此构造函数模板比​​涉及移动构造函数和用户定义的转换运算符的转换序列更匹配。因此重载解析选择构造函数模板,该模板随后无法编译,因为f不是Callable

在 C++14 中,构造函数模板可能会出现替换失败,因为f不是Callable。因此,构造函数模板不参与重载解析,并且剩余的最佳匹配是涉及std::function移动构造函数和用户定义的转换运算符的转换序列,因此使用该序列。


Clang 在 C++11 和 C++14 模式下编译您的测试用例。GCC 在 C++11 和 C++14 模式下拒绝您的测试用例。这是另一个测试用例,演示了 GCC 中的相同问题:

#include <type_traits>

struct A {
  void operator()() const {}
};
struct B {
  void operator()() const {}
};
struct C : A, B {};

template <typename F, typename = std::result_of_t<F&()>>
void test(int) {}

template <typename F>
void test(double) {}

int main() {
  test<C>(42);
}
Run Code Online (Sandbox Code Playgroud)

test(int)不应该参与重载决策,因为std::result_of_t<F&()>应该是替换失败,所以test(double)应该被调用。然而,这段代码无法在 GCC 中编译。

这与您的测试用例中看到的问题相同,因为这与在 libstdc++ 的构造函数模板中实现 SFINAE 的机制相同std::function