2016-11-20 31 views
7

此代码失败,并克++ 4.9和更高版本(包括从SVN当前建立,),但没有与铛++和微软的编译器警告编译(从VS2015)为什么g ++不能从具有转换运算符和不可访问的函数调用运算符的类型的std :: function <>的init初始化?

#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{}}; 
} 

main()f的构造失败由于operator()是在结构C中不明确。

为什么g ++认为这是不明确的? C中的函数调用运算符是私有继承的,不可访问。 向结构C添加一个私有或明确删除的void operator()() const,使代码按照预期编译和使用转换运算符。为什么这些无法进入的运营商在难以进入的遗传运营商做什么时不会造成问题?

+0

这很有道理@ildjarn。所以铿锵++和ms编译器都接受了代码错了吗? – rollbear

+1

实际上我不确定;访问检查确实是在重载解析之后执行的,但我真的不知道'C'的转换操作符在哪里进入。我的直觉说海湾合作委员会是正确的,但我不会支持太多...有一个upvote,很好的问题。 ; - ] – ildjarn

+4

*私人继承和不可见*不正确,'私人'并不意味着不可见,这意味着无法访问。重载解决后检查访问。 –

回答

3

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

C++ 11:

fCallable为参数类型ArgTypes和返回类型R

C++ 14:

不得参加重载除非fCallable为参数类型ArgTypes...和返回类型R


在C++ 11,此构造模板是不是涉及std::function移动构造函数和用户定义的转换操作符转换序列更好的匹配。因此,重载解析选择构造函数模板,然后编译失败,因为f不是Callable

在C++ 14中,构造函数模板受替换失败的影响,因为f不是Callable。因此,构造函数模板不参与重载解析,最好的剩余匹配是涉及移动构造函数和用户定义的转换运算符(因此使用该转换运算符)的转换序列。


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); 
} 

test(int)不应参与重载决议,因为std::result_of_t<F&()>应该是置换失败,所以test(double)应该叫。但是,此代码无法在GCC中编译。

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

+0

我的真实情况是C++ 14。我标记了C++ 11,因为我看到了相同的错误,不期望规则发生了变化。你碰巧有一个海湾合作委员会的bug数? – rollbear

+1

'私人'是无关紧要的。 GCC似乎将这种模棱两可的查找视为一个硬错误,但仅限于函数调用表达式。 –

2

铛之间和g ++似乎是由于SFINAE一些模糊性的差++:

...

取代失败是当类型或表达式 上面会任何情况如果使用替代参数写成0​​,则会形成不规则(具有所需的诊断)。

只有在类型和表达式的函数类型或它的模板参数类型的立即 背景的失败是 SFINAE错误。如果对替代类型/表达式 的评估导致副作用,例如某些模板的特化实例化,隐式定义成员函数的生成, 等,这些副作用中的错误被视为硬错误

现在,std::function<R(Args...)>provides a template constructor

template<class F> function(F f); 

这(通过SFINAE的利用率)

不参与重载解析,除非fCallable的参数类型Args...和返回类型R

struct C从你的例子不是Callable类型,因为operator()的模糊性(事实上,后者则是C无法进入这个阶段重载的不发挥任何作用)的。因此,取决于标准库中的is-this-type-callable检查的执行情况,您的代码可能会编译或可能不会编译。这可以解释g ++和msvc之间的区别,但不是clang ++和g ++之间的区别,它们使用相同的标准库,工作方式不同 - clang ++编译代码,而g ++不会。我不排除C++标准严格定义其中哪些是正确的可能性,但是恕我直言,它超出了大多数C++程序员应该能够自己弄清楚的程度。

+0

“*不参与重载解析,除非'f'是['Callable'] [3]参数类型'参数...'和返回类型'R' *”这不仅适用于C++ 14向前?这个问题被标记为“C++ 11”。 – ildjarn

1

在C++ 11,std::function具有通用构造

template<class F> 
function(F f) 

认为 “盲目地” 试图治疗f作为调用对象。

如果其他构造函数不是,则选择它(相当贪婪)。

private基地不应该被要求打破你的代码。

在C++ 14中,只有在某些情况下才考虑这种重载的规则。看来你的C++ 11编译器正在执行这样的规则,但是私有继承混淆了它。但是,这不在问题的范围之内。

这里是std::function 1个参数构造:

function(std::nullptr_t); 
function(const function& other); 
function(function&& other); 
template< class F > 
function(F f); 

一个你想要的

function(function&& other); 

要求在

template< class F > 
function(F f); 

用户定义的转换所以这应该适用,然后编译失败。

相关问题