4

我在创建一个简单的 RAII 包装器时遇到了一个意想不到的问题。

更不用说下面代码的逻辑不完整(复制构造函数和赋值运算符未删除等,这意味着是一个 SSCCE),让我印象深刻的是我的包装器的复制初始化与临时 lambda 导致编译错误,而直接初始化则没有。

这种行为可以在 GCC 4.7.2 和 Clang 3.2 上观察到,而 ICC 13.0.1 和 VC10 编译这两个版本都没有问题。

#include <iostream>
#include <functional>

using namespace std;

struct A
{
    template<typename F>
    A(F&& f) : _f(forward<F>(f)) { }

    ~A() { _f(); }

private:

    std::function<void()> _f;
};

int main()
{
    // A a = [] () { cout << "Hello" << endl; }; // ERROR!
    A a([] () { cout << "Hello" << endl; }); // OK
}

谁是对的,错的有什么问题?这是 C++ 标准库的实现问题,还是编译器问题?

特别欢迎对 C++11 标准的引用。

编辑:

这是 Clang 3.2 产生的错误:

Compilation finished with errors:

In file included from source.cpp:2:
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/functional:1925:2: error: type 'A' does not provide a call operator
    (*_Base::_M_get_pointer(__functor))(
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../../include/c++/4.7/functional:2297:33: note: in instantiation of member function 'std::_Function_handler<void (), A>::_M_invoke' requested here
    _M_invoker = &_My_handler::_M_invoke;
                                   ^
source.cpp:9:16: note: in instantiation of function template specialization 'std::function<void ()>::function<A>' requested here
    A(F&& f) : _f(forward<F>(f)) { }
               ^
source.cpp:20:7: note: in instantiation of function template specialization 'A::A<A>' requested here
    A a = [] () { cout << "Hello" << endl; }; // ERROR!
      ^

产生 1 个错误。

4

1 回答 1

3

错误消息(gcc 4.7.2)提供了合理的信息:

c++/4.7/functional: In instantiation of 'static void std::_Function_handler<void(_ArgTypes ...), _Functor>::_M_invoke(const std::_Any_data&, _ArgTypes ...) [with _Functor = A; _ArgTypes = {}]':
c++/4.7/functional:2298:6:   required from 'std::function<_Res(_ArgTypes ...)>::function(_Functor, typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type) [with _Functor = A; _Res = void; _ArgTypes = {}; typename std::enable_if<(! std::is_integral<_Functor>::value), std::function<_Res(_ArgTypes ...)>::_Useless>::type = std::function<void()>::_Useless]'
source.cpp:9:32:   required from 'A::A(F&&) [with F = A]'
source.cpp:22:44:   required from here
c++/4.7/functional:1926:2: error: no match for call to '(A) ()'

问题是您的类没有可用于复制初始化的隐式移动构造函数。隐式定义的移动构造函数被删除,因为A有一个用户定义的析构函数(12.8p9b4)。

添加:

A(A &&) = default;

请注意,由于选择了默认的移动构造函数,因此析构函数应检查它_f是否为非空;由于移动构造函数std::function不保证目标为空,您还应该自己执行该更改:

A(A &&a): _f() { std::swap(_f, a._f); }
~A() { if (_f) _f(); }

回想一下(根据 8.5p17),复制初始化涉及创建一个 prvalue 临时,然后用于直接初始化目标对象。选择是在模板构造函数和隐式定义的复制构造函数之间;具有模板参数类型的模板构造函数A是首选,因为它比A &&绑定到纯右值A更好const A &

另一种选择(可能更好)是禁用参数的模板构造A函数:

template<typename F, typename = typename std::enable_if<!std::is_same<F, A>::value>::type>
A(F&& f) : _f(forward<F>(f)) { }

在这种情况下,将选择隐式定义的复制构造函数,因此析构函数不需要检查 ; 的状态_f。但是,如果编译器不执行复制省略,那么它 (and _f) 将被调用两次。

Copy-elision is allowed (12.8p31); the non-elided form must be accessible (12.8p32) but as far as I can tell (and by omission) the compiler is not required to check that it is compilable. So it is permissible for a compiler to either compile or refuse to compile the program; if it does compile, though, it must have performed copy elision.

于 2013-02-07T17:05:51.737 回答