将成员函数从基类移到派生类不会明显破坏程序 [英] Moving a member function from base class to derived class breaks the program for no obvious reason

查看:53
本文介绍了将成员函数从基类移到派生类不会明显破坏程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个(虚构的)问题最初被形容为一个谜题,隐瞒了 一些可能有助于更快发现问题的细节.向下滚动 适用于更简单的 MCVE 版本.

This (made-up) question was initially formulated as a puzzle, concealing some of the details that might help seeing the problem faster. Scroll down for the simpler MCVE version.


原始版本(拼图游戏)

我有这段输出0的代码:


Original (a-la puzzle) version

I have this piece of code that outputs 0:

#include <iostream>
#include <regex>

using namespace std;

regex sig_regex("[0-9]+");
bool oldmode = false;

template<class T>
struct B
{
    T bitset;

    explicit B(T flags) : bitset(flags) {}

    bool foo(T n, string s)
    {
        return bitset < 32                   // The mouth is not full of teeth
               && 63 > (~n & 255) == oldmode // Fooness holds
               && regex_match(s, sig_regex); // Signature matches
    }
};

template<class T>
struct D : B<T>
{
    D(T flags) : B<T>(flags) {}

};

int main()
{
    D<uint64_t> d(128 | 16 | 1);
    cout << d.foo(7, "123") << endl;
}

但是,当我将函数foo()B移到D时,它开始输出1(证明在Coliru上).

However, when I move the function foo() from B to D it starts outputting 1 (proof is on Coliru).

为什么会这样?

在Coliru上生活

#include <iostream>
#include <bitset>

using namespace std;

template<class T>
struct B
{
    T bitset{0};

    bool foo(int x)
    {
        return bitset < 32 && 63 > (x + 1) == x % 2;
    }
};

template<class T>
struct D : B<T>
{
    bool bar(int x) // This is identical to B<T>::foo()
    {
        return bitset < 32 && 63 > (x + 1) == x % 2;
    }
};

int main()
{
    D<uint64_t> d;
    cout << d.foo(1) << endl; // outputs 1
    cout << d.bar(1) << endl; // outputs 0; So how is bar() different from foo()?
}

推荐答案

这就是为什么您永远不要using namespace std;

This is why you should never using namespace std;

bool foo(T n, string s)
{
    return bitset < 32                  
           && 63 > (~n & 255) == oldmode 
           && regex_match(s, sig_regex);
}

那个bitset不是您想的那样.因为B<T>是从属基类,所以成员从不合格的查找中隐藏.因此,要访问bitset,您需要通过this 1 对其进行访问,或对其进行明确限定(请参阅

That bitset isn't what you think it is. Because B<T> is a dependent base class, members are hidden from unqualified lookup. So to access bitset, you need to access it through this1, or explicitly qualify it (see here for more details):

(this->bitset)
B<T>::bitset

因为在派生情况下bitset没有命名B<T>::bitset,这意味着什么?好吧,因为您编写了using namespace std;,所以它实际上是std::bitset,其余的表达式恰好是有效的.这是发生了什么:

Because bitset doesn't name B<T>::bitset in the derived case, what could it mean? Well, because you wrote using namespace std;, it's actually std::bitset, and the rest of your expression just so happens to be valid. Here's what happens:

bool foo(T n, string s)
{
    return std::bitset<32 && 63>(~n & 255) == oldmode 
           && regex_match(s, sig_regex);
}

32 && 63的计算结果为true,对于std::bitset模板参数,其被提升为1u.此std::bitset~n & 255初始化,并用oldmode检查是否相等.最后一步是有效的,因为std::bitset具有非显式构造函数,该构造函数允许从oldmode构造临时std::bitset<1>.

The 32 && 63 evaluates to true, which is promoted to 1u for the std::bitset template argument. This std::bitset is initialized with ~n & 255, and is checked for equality with oldmode. This last step is valid because std::bitset has a non-explicit constructor which allows a temporary std::bitset<1> to be constructed from oldmode.

1 注意,在这种情况下,由于一些非常清晰的解析歧义规则,我们需要在this->bitset上加上括号.有关详细信息,请参见依赖模板的基础成员未正确解析.

1 Note that we need to parenthesise this->bitset in this case due to some pretty subtle parsing disambiguity rules. See Template dependent base member is not resolved properly for details.

这篇关于将成员函数从基类移到派生类不会明显破坏程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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