C ++构造函数:完美的转发和重载 [英] C++ Constructor: Perfect forwarding and overload

查看:83
本文介绍了C ++构造函数:完美的转发和重载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个类,A和B,B是从A派生的。
A有多个构造函数(在下面的示例中为2)。
B还有一个要初始化的成员(它有一个默认的初始化程序)。

I have two classes, A and B, and B is derived from A. A has multiple constructors (2 in the example below). B has one additional member to initialize (which has a default initializer).

我如何实现可以使用其中一个来构造B A的构造函数,而不必手动重写B中A的所有构造函数重载?

(在下面的示例中,我不得不提供四个构造函数对于B: B():A(){} B(string s):A(s){} B(int b):A(),p(b){} B(string s,int b):A(s ),p(b){} ,而不仅仅是两个,至少在忽略默认参数的可能性时如此。)

(In the example below, I would otherwise have to provide four constructors for B:B():A(){}, B(string s):A(s){}, B(int b):A(),p(b){}, B(string s, int b):A(s),p(b){}, instead of just two, at least when ignoring the possibility of default arguments).

我的方法是但是,以下情况会导致错误:

My approach was perfect forwarding, however, the following scenario leads to an error:

#include <utility>
#include <string>

struct A {
    A(const std::string& a) : name(a) {}
    A(){}
    virtual ~A(){}

    std::string name;
};

struct B : public A {
    template<typename... Args>
    B(Args&&... args) : A(std::forward<Args>(args)...) {}

    B(const std::string& a, int b) : A(a), p(b) {}

    int p = 0;
};

int main()
{
    B b1("foo");
    B b2("foobar", 1);
}

对于b2,GCC抱怨没有匹配功能调用'A :: A(const char [5],int)
显然它正在尝试调用完美的转发构造函数,该函数显然不起作用,而不是B的第二个构造函数。

For b2, GCC complains about no matching function for call to 'A::A(const char [5], int). Apparently it is trying to call the perfect forwarding constructor, which obviously shouldn't work, instead of the second constructor of B.

为什么不看不到编译器的第二个构造函数,而是调用它吗?是出于技术原因,编译器找不到正确的B构造函数?
如何解决此问题?

确切的错误消息:

main.cpp: In instantiation of 'B::B(Args&& ...) [with Args = {const char (&)[5], int}]':
main.cpp:26:19:   required from here
main.cpp:15:54: error: no matching function for call to 'A::A(const char [5], int)'
     B(Args&&... args) : A(std::forward<Args>(args)...) {}
                                                      ^
main.cpp:6:5: note: candidate: A::A()
     A(){}
     ^
main.cpp:6:5: note:   candidate expects 0 arguments, 2 provided
main.cpp:5:5: note: candidate: A::A(const string&)
     A(const std::string& a) : name(a) {}
     ^
main.cpp:5:5: note:   candidate expects 1 argument, 2 provided
main.cpp:4:8: note: candidate: A::A(const A&)
 struct A {
        ^
main.cpp:4:8: note:   candidate expects 1 argument, 2 provided


推荐答案

foobar const char(&)[7] 。因此, Args const std :: string&

因此,选择了此重载:

template<typename... Args>
B(Args&&... args) : A(std::forward<Args>(args)...) {}

其中 Args const char(&)[7]

因此变为:

B(const char (&&args_0) [7], int&& args_1)

并转发到 A 的2参数构造函数...不存在。

which is forwarded to A's 2-argument constructor... which does not exist.


通缉犯行为是,如果使用适用于A的构造函数构造B,则将调用B的 ... Args构造函数,否则将调用B的另一个构造函数,否则将失败,并且找不到适用于B的适当构造函数。

The wanted behavior is that if you construct a B with a constructor that works for A then the "...Args constructor" of B is called, otherwise another constructor of B gets called, otherwise it fails with "no appropriate constructor for B found".

类似这样的东西...

something like this...

#include <utility>
#include <string>

struct A {
    A(std::string a) : name(std::move(a)) {}
    A(){}
    virtual ~A(){}

    std::string name;
};

template<class...T> struct can_construct_A
{
    template<class...Args> static auto test(Args&&...args)
    -> decltype(A(std::declval<Args>()...), void(), std::true_type());

    template<class...Args> static auto test(...) -> std::false_type;

    using type = decltype(test(std::declval<T>()...));
    static constexpr bool value = decltype(test(std::declval<T>()...))::value;
};

struct B : public A {

    template<class...Args>
    B(std::true_type a_constructable, Args&&...args)
    : A(std::forward<Args>(args)...)
    {}

    template<class Arg1, class Arg2>
    B(std::false_type a_constructable, Arg1&& a1, Arg2&& a2)
    : A(std::forward<Arg1>(a1))
    , p(std::forward<Arg2>(a2))
    {
    }

    template<typename... Args>
    B(Args&&... args)
    : B(typename can_construct_A<Args&&...>::type()
        , std::forward<Args>(args)...) {}

    int p = 0;
};

int main()
{
    B b1("foo");
    B b2("foobar", 1);
}




在看到A没有一个匹配的构造函数,为什么不返回并继续寻找可能匹配的B的其他构造函数?出于技术原因吗?

After seeing that A doesn't have a matching constructor, why doesn't it go back and continue looking for other constructors of B that might match? Are there technical reasons?

简而言之(很简单地说),当发生重载解析时,编译器将执行以下操作:

In a nutshell (and putting it very simply), when overload resolution takes place the compiler does the following:


  1. 扩展所有可能与给定参数匹配的模板重载。将它们添加到列表中(权重指示到达那里所涉及的专业化水平。)

  1. expand all templated overloads that can possibly match the arguments given. Add them to a list (with a weight indicating the level of specialisation that was involved in getting there).

将任何具体的重载添加到列表中由合法地将转换运算符应用于参数得出,权重表示将提供的参数转换为函数参数类型所需的转换次数。

add any concrete overloads to the list that can be arrived at by legally apply conversion operators to the arguments, with a weight indicating how many conversions are required to turn the supplied arguments into the function argument types.

sort

选择那些需要最少工作的清单。如果有一条领带是最合适的,那就出错了。

pick the one that requires least work. If there is a tie for which one is best, error.

编译器对此一one而就。这不是递归搜索。

The compiler gets one go at this. It's not a recursive search.

我向在我们中间的纯粹主义者表示歉意,他们会发现这种幼稚的解释令人反感:-)

My apologies in advance to the purists amongst us who will find this childish explanation offensive :-)

这篇关于C ++构造函数:完美的转发和重载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆