1

代码

#include <iostream>

using namespace std;

#define PF         cout << __PRETTY_FUNCTION__ << endl;

class berlp {
public:
    berlp() { }
    void p() { }
};

template <typename T>
class derp {
public:
    derp() = default;

    derp(const T & a) : mem(a) {
        a.p();
        mem.p();

        PF
    }

    template <typename U>
    derp(U && a) : mem(std::forward<U>(a)) {
        PF
    }

    T       mem;
};

int main(int argc, const char * argv[])
{
    berlp                   one;
    derp<berlp &>           f(one);     // problems with this list below
    derp<const berlp &>     h(one);     // problem with this follows

    return 0;
}

使用 XCode 和 CLang 输出 这一切都编译得很好,这是输出...

derp<berlp &>::derp(const T &) [T = berlp &]
derp<const berlp &>::derp(U &&) [T = const berlp &, U = berlp &]

问题

derp<berlp &> f(one);:derp 构造函数中的 ap() 应该失败,因为在引用折叠后“a”是“const berlp &”,并且 p() 不是 const。其次,用“a”(const berlp &)初始化“mem”(berlp &)不应该工作。似乎“derp(const T & a)”中的“const”没有做任何事情。最后,为什么它甚至使用第一个构造函数而不是模板化的构造函数,它似乎可以在不破坏 const 的情况下完成所有这些工作?

derp<const berlp &> h(one);: 为什么这个调用使用模板化的构造函数,而另一个似乎正是我们所追求的?这不是一个太可怕的问题,因为它似乎没有破坏任何东西,但它确实允许您修改构造函数中传递的 berlp,而另一个构造函数(据说)不应该。

所以,我要么非常困惑,要么发生了什么事!请帮忙!

4

1 回答 1

1

这里确实有多个问题:

  1. 为什么derp<berlp&>(one)使用第一个构造函数?

    构造函数的声明是derp(T const&)变成derp(berlp& const&)并被折叠成derp(berlp&)的,因为没有const引用或引用引用之类的东西。这在 8.3.2 [dcl.ref] 第 6 段中说明:

    如果 typedef (7.1.3)、类型模板参数 (14.3.1) 或 decltype-specifier (7.1.6.2) 表示类型 TR 是对类型 T 的引用,则尝试创建类型“ lvalue reference to cv TR”创建类型“lvalue reference to T”,而尝试创建类型“rvalue reference to cv TR”创建类型TR。

    显然,将 a 传递berlp&给采用 a 的构造函数berlp&是完全匹配的,模板构造函数不能做得更好。因此,选择了非模板构造函数。

  2. 为什么derp<berlp&>(one)berlp工作调用?

    这里没有真正的惊喜:memis 类型berlp&并用 a 初始化,berlp&因此非const成员都按预期工作。

  3. 当使用derp<berlp const&>和传递一个berlp&模板构造函数时,它是一个完美的匹配,显然是被选中的。type 的成员变量berlp const&只是用berlp&隐式转换为 a 的 a初始化berlp const&。这里也不足为奇。

我认为您对参考折叠规则有点困惑。把它const放在错误的位置也无济于事:把它放在右边实际上应该清楚地消除大部分混乱,这是我把它放在右边的偏好const的一部分。

于 2013-10-01T05:14:42.153 回答