代码

#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构造函数中的a.p()应该失败,因为引用折叠后“a”是“const berlp&”,而p()不是const。其次,用“a”(const berlp&)初始化“mem”(berlp&)不起作用。似乎“derp(const T&a)”中的“const”什么也没做。最后,为什么它甚至使用第一个构造函数而不使用模板化的构造函数,而后者似乎可以在不破坏const的情况下完成所有这些工作?
derp<const berlp &> h(one);:当另一个似乎正是我们所追求的时,为什么此调用使用模板化构造函数?这并不是一个太可怕的问题,因为它似乎并没有破坏任何东西,但是它的确可以让您修改构造函数中传递的berlp,而其他构造函数(不应)应该不这样做。

所以,我要么很困惑,要么出事了!请帮忙!

最佳答案

这里确实有多个问题:

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

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



    显然,将berlp&传递给采用berlp&的构造函数是完全匹配的,并且模板构造函数无法做得更好。因此,选择了非模板构造函数。
  • 为什么用derp<berlp&>(one)调用berlp起作用?

    这里没有真正的惊喜:mem的类型为berlp&,并使用berlp&进行了初始化,因此所有非const成员均按预期工作。
  • 当使用derp<berlp const&>并传递berlp&时,模板构造函数是一个完美的选择,显然是选择了。刚刚使用berlp const&初始化berlp&类型的成员变量,该隐式转换为berlp const&。也不奇怪。

  • 我认为您对参考折叠规则有些困惑。将const放在错误的位置也无济于事:将const放到正确的位置实际上应该可以清楚地消除大部分混乱,这是my preference的一部分,可以将ojit_code放到正确的位置。

    09-11 17:32